at path:ROOT / wp-includes / ID3 / getid3.php
run:R W Run
54.21 KB
2026-03-11 16:18:51
R W Run
79.05 KB
2026-03-11 16:18:51
R W Run
1.36 KB
2026-03-11 16:18:51
R W Run
133.61 KB
2026-03-11 16:18:51
R W Run
26.5 KB
2026-03-11 16:18:51
R W Run
104.64 KB
2026-03-11 16:18:51
R W Run
164.97 KB
2026-03-11 16:18:51
R W Run
136.53 KB
2026-03-11 16:18:51
R W Run
38.46 KB
2026-03-11 16:18:51
R W Run
10.63 KB
2026-03-11 16:18:51
R W Run
19.23 KB
2026-03-11 16:18:51
R W Run
104.5 KB
2026-03-11 16:18:51
R W Run
42.74 KB
2026-03-11 16:18:51
R W Run
18.63 KB
2026-03-11 16:18:51
R W Run
14.7 KB
2026-03-11 16:18:51
R W Run
151.2 KB
2026-03-11 16:18:51
R W Run
11.75 KB
2026-03-11 16:18:51
R W Run
25.71 KB
2026-03-11 16:18:51
R W Run
error_log
📄getid3.php
1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org> //
4// available at https://github.com/JamesHeinrich/getID3 //
5// or https://www.getid3.org //
6// or http://getid3.sourceforge.net //
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
13if (!defined('GETID3_OS_ISWINDOWS')) {
14 define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
15}
16// Get base path of getID3() - ONCE
17if (!defined('GETID3_INCLUDEPATH')) {
18 define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
19}
20if (!defined('ENT_SUBSTITUTE')) { // PHP5.3 adds ENT_IGNORE, PHP5.4 adds ENT_SUBSTITUTE
21 define('ENT_SUBSTITUTE', (defined('ENT_IGNORE') ? ENT_IGNORE : 8));
22}
23
24/*
25https://www.getid3.org/phpBB3/viewtopic.php?t=2114
26If you are running into a the problem where filenames with special characters are being handled
27incorrectly by external helper programs (e.g. metaflac), notably with the special characters removed,
28and you are passing in the filename in UTF8 (typically via a HTML form), try uncommenting this line:
29*/
30//setlocale(LC_CTYPE, 'en_US.UTF-8');
31
32// attempt to define temp dir as something flexible but reliable
33$temp_dir = ini_get('upload_tmp_dir');
34if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
35 $temp_dir = '';
36}
37if (!$temp_dir && function_exists('sys_get_temp_dir')) { // sys_get_temp_dir added in PHP v5.2.1
38 // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
39 $temp_dir = sys_get_temp_dir();
40}
41$temp_dir = @realpath($temp_dir); // see https://github.com/JamesHeinrich/getID3/pull/10
42$open_basedir = ini_get('open_basedir');
43if ($open_basedir) {
44 // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
45 $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
46 $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
47 if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
48 $temp_dir .= DIRECTORY_SEPARATOR;
49 }
50 $found_valid_tempdir = false;
51 $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
52 foreach ($open_basedirs as $basedir) {
53 if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
54 $basedir .= DIRECTORY_SEPARATOR;
55 }
56 if (strpos($temp_dir, $basedir) === 0) {
57 $found_valid_tempdir = true;
58 break;
59 }
60 }
61 if (!$found_valid_tempdir) {
62 $temp_dir = '';
63 }
64 unset($open_basedirs, $found_valid_tempdir, $basedir);
65}
66if (!$temp_dir) {
67 $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
68}
69// $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
70if (!defined('GETID3_TEMP_DIR')) {
71 define('GETID3_TEMP_DIR', $temp_dir);
72}
73unset($open_basedir, $temp_dir);
74
75// End: Defines
76
77
78class getID3
79{
80 /*
81 * Settings
82 */
83
84 /**
85 * CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
86 *
87 * @var string
88 */
89 public $encoding = 'UTF-8';
90
91 /**
92 * Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
93 *
94 * @var string
95 */
96 public $encoding_id3v1 = 'ISO-8859-1';
97
98 /**
99 * ID3v1 should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'Windows-1251' or 'KOI8-R'. If true attempt to detect these encodings, but may return incorrect values for some tags actually in ISO-8859-1 encoding
100 *
101 * @var bool
102 */
103 public $encoding_id3v1_autodetect = false;
104
105 /*
106 * Optional tag checks - disable for speed.
107 */
108
109 /**
110 * Read and process ID3v1 tags
111 *
112 * @var bool
113 */
114 public $option_tag_id3v1 = true;
115
116 /**
117 * Read and process ID3v2 tags
118 *
119 * @var bool
120 */
121 public $option_tag_id3v2 = true;
122
123 /**
124 * Read and process Lyrics3 tags
125 *
126 * @var bool
127 */
128 public $option_tag_lyrics3 = true;
129
130 /**
131 * Read and process APE tags
132 *
133 * @var bool
134 */
135 public $option_tag_apetag = true;
136
137 /**
138 * Copy tags to root key 'tags' and encode to $this->encoding
139 *
140 * @var bool
141 */
142 public $option_tags_process = true;
143
144 /**
145 * Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
146 *
147 * @var bool
148 */
149 public $option_tags_html = true;
150
151 /*
152 * Optional tag/comment calculations
153 */
154
155 /**
156 * Calculate additional info such as bitrate, channelmode etc
157 *
158 * @var bool
159 */
160 public $option_extra_info = true;
161
162 /*
163 * Optional handling of embedded attachments (e.g. images)
164 */
165
166 /**
167 * Defaults to true (ATTACHMENTS_INLINE) for backward compatibility
168 *
169 * @var bool|string
170 */
171 public $option_save_attachments = true;
172
173 /*
174 * Optional calculations
175 */
176
177 /**
178 * Get MD5 sum of data part - slow
179 *
180 * @var bool
181 */
182 public $option_md5_data = false;
183
184 /**
185 * Use MD5 of source file if available - only FLAC and OptimFROG
186 *
187 * @var bool
188 */
189 public $option_md5_data_source = false;
190
191 /**
192 * Get SHA1 sum of data part - slow
193 *
194 * @var bool
195 */
196 public $option_sha1_data = false;
197
198 /**
199 * Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on
200 * PHP_INT_MAX)
201 *
202 * @var bool|null
203 */
204 public $option_max_2gb_check;
205
206 /**
207 * Read buffer size in bytes
208 *
209 * @var int
210 */
211 public $option_fread_buffer_size = 32768;
212
213
214
215 // module-specific options
216
217 /** archive.rar
218 * if true use PHP RarArchive extension, if false (non-extension parsing not yet written in getID3)
219 *
220 * @var bool
221 */
222 public $options_archive_rar_use_php_rar_extension = true;
223
224 /** archive.gzip
225 * Optional file list - disable for speed.
226 * Decode gzipped files, if possible, and parse recursively (.tar.gz for example).
227 *
228 * @var bool
229 */
230 public $options_archive_gzip_parse_contents = false;
231
232 /** audio.midi
233 * if false only parse most basic information, much faster for some files but may be inaccurate
234 *
235 * @var bool
236 */
237 public $options_audio_midi_scanwholefile = true;
238
239 /** audio.mp3
240 * Forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow,
241 * unrecommended, but may provide data from otherwise-unusable files.
242 *
243 * @var bool
244 */
245 public $options_audio_mp3_allow_bruteforce = false;
246
247 /** audio.mp3
248 * number of frames to scan to determine if MPEG-audio sequence is valid
249 * Lower this number to 5-20 for faster scanning
250 * Increase this number to 50+ for most accurate detection of valid VBR/CBR mpeg-audio streams
251 *
252 * @var int
253 */
254 public $options_audio_mp3_mp3_valid_check_frames = 50;
255
256 /** audio.wavpack
257 * Avoid scanning all frames (break after finding ID_RIFF_HEADER and ID_CONFIG_BLOCK,
258 * significantly faster for very large files but other data may be missed
259 *
260 * @var bool
261 */
262 public $options_audio_wavpack_quick_parsing = false;
263
264 /** audio-video.flv
265 * Break out of the loop if too many frames have been scanned; only scan this
266 * many if meta frame does not contain useful duration.
267 *
268 * @var int
269 */
270 public $options_audiovideo_flv_max_frames = 100000;
271
272 /** audio-video.matroska
273 * If true, do not return information about CLUSTER chunks, since there's a lot of them
274 * and they're not usually useful [default: TRUE].
275 *
276 * @var bool
277 */
278 public $options_audiovideo_matroska_hide_clusters = true;
279
280 /** audio-video.matroska
281 * True to parse the whole file, not only header [default: FALSE].
282 *
283 * @var bool
284 */
285 public $options_audiovideo_matroska_parse_whole_file = false;
286
287 /** audio-video.quicktime
288 * return all parsed data from all atoms if true, otherwise just returned parsed metadata
289 *
290 * @var bool
291 */
292 public $options_audiovideo_quicktime_ReturnAtomData = false;
293
294 /** audio-video.quicktime
295 * return all parsed data from all atoms if true, otherwise just returned parsed metadata
296 *
297 * @var bool
298 */
299 public $options_audiovideo_quicktime_ParseAllPossibleAtoms = false;
300
301 /** audio-video.swf
302 * return all parsed tags if true, otherwise do not return tags not parsed by getID3
303 *
304 * @var bool
305 */
306 public $options_audiovideo_swf_ReturnAllTagData = false;
307
308 /** graphic.bmp
309 * return BMP palette
310 *
311 * @var bool
312 */
313 public $options_graphic_bmp_ExtractPalette = false;
314
315 /** graphic.bmp
316 * return image data
317 *
318 * @var bool
319 */
320 public $options_graphic_bmp_ExtractData = false;
321
322 /** graphic.png
323 * If data chunk is larger than this do not read it completely (getID3 only needs the first
324 * few dozen bytes for parsing).
325 *
326 * @var int
327 */
328 public $options_graphic_png_max_data_bytes = 10000000;
329
330 /** misc.pdf
331 * return full details of PDF Cross-Reference Table (XREF)
332 *
333 * @var bool
334 */
335 public $options_misc_pdf_returnXREF = false;
336
337 /** misc.torrent
338 * Assume all .torrent files are less than 1MB and just read entire thing into memory for easy processing.
339 * Override this value if you need to process files larger than 1MB
340 *
341 * @var int
342 */
343 public $options_misc_torrent_max_torrent_filesize = 1048576;
344
345
346
347 // Public variables
348
349 /**
350 * Filename of file being analysed.
351 *
352 * @var string
353 */
354 public $filename;
355
356 /**
357 * Filepointer to file being analysed.
358 *
359 * @var resource
360 */
361 public $fp;
362
363 /**
364 * Result array.
365 *
366 * @var array
367 */
368 public $info;
369
370 /**
371 * @var string
372 */
373 public $tempdir = GETID3_TEMP_DIR;
374
375 /**
376 * @var int
377 */
378 public $memory_limit = 0;
379
380 /**
381 * @var string
382 */
383 protected $startup_error = '';
384
385 /**
386 * @var string
387 */
388 protected $startup_warning = '';
389
390 const VERSION = '1.9.24-202509040923';
391 const FREAD_BUFFER_SIZE = 32768;
392
393 const ATTACHMENTS_NONE = false;
394 const ATTACHMENTS_INLINE = true;
395
396 /**
397 * @throws getid3_exception
398 */
399 public function __construct() {
400
401 // Check for PHP version
402 $required_php_version = '5.3.0';
403 if (version_compare(PHP_VERSION, $required_php_version, '<')) {
404 $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION."\n";
405 return;
406 }
407
408 // Check memory
409 $memoryLimit = ini_get('memory_limit');
410 if (preg_match('#([0-9]+) ?M#i', $memoryLimit, $matches)) {
411 // could be stored as "16M" rather than 16777216 for example
412 $memoryLimit = (int) $matches[1] * 1048576;
413 } elseif (preg_match('#([0-9]+) ?G#i', $memoryLimit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
414 // could be stored as "2G" rather than 2147483648 for example
415 $memoryLimit = (int) $matches[1] * 1073741824;
416 }
417 $this->memory_limit = $memoryLimit;
418
419 if ($this->memory_limit <= 0) {
420 // memory limits probably disabled
421 } elseif ($this->memory_limit <= 4194304) {
422 $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini'."\n";
423 } elseif ($this->memory_limit <= 12582912) {
424 $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'."\n";
425 }
426
427 // Check safe_mode off
428 if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
429 $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
430 }
431
432 if (($mbstring_func_overload = (int) ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) {
433 // http://php.net/manual/en/mbstring.overload.php
434 // "mbstring.func_overload in php.ini is a positive value that represents a combination of bitmasks specifying the categories of functions to be overloaded. It should be set to 1 to overload the mail() function. 2 for string functions, 4 for regular expression functions"
435 // getID3 cannot run when string functions are overloaded. It doesn't matter if mail() or ereg* functions are overloaded since getID3 does not use those.
436 $this->startup_error .= 'WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", getID3 cannot run with this setting (bitmask 2 (string functions) cannot be set). Recommended to disable entirely.'."\n";
437 }
438
439 // check for magic quotes in PHP < 5.4.0 (when these options were removed and getters always return false)
440 if (version_compare(PHP_VERSION, '5.4.0', '<')) {
441 // Check for magic_quotes_runtime
442 if (function_exists('get_magic_quotes_runtime')) {
443 if (get_magic_quotes_runtime()) { // @phpstan-ignore-line
444 $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).'."\n";
445 }
446 }
447 // Check for magic_quotes_gpc
448 if (function_exists('get_magic_quotes_gpc')) {
449 if (get_magic_quotes_gpc()) {
450 $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).'."\n";
451 }
452 }
453 }
454
455 // Load support library
456 if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
457 $this->startup_error .= 'getid3.lib.php is missing or corrupt'."\n";
458 }
459
460 if ($this->option_max_2gb_check === null) {
461 $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
462 }
463
464
465 // Needed for Windows only:
466 // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
467 // as well as other helper functions such as head, etc
468 // This path cannot contain spaces, but the below code will attempt to get the
469 // 8.3-equivalent path automatically
470 // IMPORTANT: This path must include the trailing slash
471 if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
472
473 $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
474
475 if (!is_dir($helperappsdir)) {
476 $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'."\n";
477 } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
478 $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
479 $path_so_far = array();
480 foreach ($DirPieces as $key => $value) {
481 if (strpos($value, ' ') !== false) {
482 if (!empty($path_so_far)) {
483 $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
484 $dir_listing = shell_exec($commandline);
485 $lines = explode("\n", $dir_listing);
486 foreach ($lines as $line) {
487 $line = trim($line);
488 if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
489 list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
490 if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
491 $value = $shortname;
492 }
493 }
494 }
495 } else {
496 $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.'."\n";
497 }
498 }
499 $path_so_far[] = $value;
500 }
501 $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
502 }
503 define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
504 }
505
506 if (!empty($this->startup_error)) {
507 echo $this->startup_error;
508 throw new getid3_exception($this->startup_error);
509 }
510 }
511
512 /**
513 * @return string
514 */
515 public function version() {
516 return self::VERSION;
517 }
518
519 /**
520 * @return int
521 */
522 public function fread_buffer_size() {
523 return $this->option_fread_buffer_size;
524 }
525
526 /**
527 * @param array $optArray
528 *
529 * @return bool
530 */
531 public function setOption($optArray) {
532 if (empty($optArray)) {
533 return false;
534 }
535 foreach ($optArray as $opt => $val) {
536 if (isset($this->$opt) === false) {
537 continue;
538 }
539 $this->$opt = $val;
540 }
541 return true;
542 }
543
544 /**
545 * @param string $filename
546 * @param int $filesize
547 * @param resource $fp
548 *
549 * @return bool
550 *
551 * @throws getid3_exception
552 */
553 public function openfile($filename, $filesize=null, $fp=null) {
554 try {
555 if (!empty($this->startup_error)) {
556 throw new getid3_exception($this->startup_error);
557 }
558 if (!empty($this->startup_warning)) {
559 foreach (explode("\n", $this->startup_warning) as $startup_warning) {
560 $this->warning($startup_warning);
561 }
562 }
563
564 // init result array and set parameters
565 $this->filename = $filename;
566 $this->info = array();
567 $this->info['GETID3_VERSION'] = $this->version();
568 $this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
569
570 // remote files not supported
571 if (preg_match('#^(ht|f)tps?://#', $filename)) {
572 throw new getid3_exception('Remote files are not supported - please copy the file locally first');
573 }
574
575 $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
576 //$filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename);
577
578 // open local file
579 //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see https://www.getid3.org/phpBB3/viewtopic.php?t=1720
580 if (($fp != null) && ((get_resource_type($fp) == 'file') || (get_resource_type($fp) == 'stream'))) {
581 $this->fp = $fp;
582 } elseif ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
583 // great
584 } else {
585 $errormessagelist = array();
586 if (!is_readable($filename)) {
587 $errormessagelist[] = '!is_readable';
588 }
589 if (!is_file($filename)) {
590 $errormessagelist[] = '!is_file';
591 }
592 if (!file_exists($filename)) {
593 $errormessagelist[] = '!file_exists';
594 }
595 if (empty($errormessagelist)) {
596 $errormessagelist[] = 'fopen failed';
597 }
598 throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
599 }
600
601 $this->info['filesize'] = (!is_null($filesize) ? $filesize : filesize($filename));
602 // set redundant parameters - might be needed in some include file
603 // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
604 $filename = str_replace('\\', '/', $filename);
605 $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
606 $this->info['filename'] = getid3_lib::mb_basename($filename);
607 $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
608
609 // set more parameters
610 $this->info['avdataoffset'] = 0;
611 $this->info['avdataend'] = $this->info['filesize'];
612 $this->info['fileformat'] = ''; // filled in later
613 $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
614 $this->info['video']['dataformat'] = ''; // filled in later, unset if not used
615 $this->info['tags'] = array(); // filled in later, unset if not used
616 $this->info['error'] = array(); // filled in later, unset if not used
617 $this->info['warning'] = array(); // filled in later, unset if not used
618 $this->info['comments'] = array(); // filled in later, unset if not used
619 $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
620
621 // option_max_2gb_check
622 if ($this->option_max_2gb_check) {
623 // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
624 // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
625 // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
626 $fseek = fseek($this->fp, 0, SEEK_END);
627 if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
628 ($this->info['filesize'] < 0) ||
629 (ftell($this->fp) < 0)) {
630 $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
631
632 if ($real_filesize === false) {
633 unset($this->info['filesize']);
634 fclose($this->fp);
635 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.');
636 } elseif (getid3_lib::intValueSupported($real_filesize)) {
637 unset($this->info['filesize']);
638 fclose($this->fp);
639 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 / 1073741824, 3).'GB, please report to info@getid3.org');
640 }
641 $this->info['filesize'] = $real_filesize;
642 $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize / 1073741824, 3).'GB) and is not properly supported by PHP.');
643 }
644 }
645
646 return true;
647
648 } catch (Exception $e) {
649 $this->error($e->getMessage());
650 }
651 return false;
652 }
653
654 /**
655 * analyze file
656 *
657 * @param string $filename
658 * @param int $filesize
659 * @param string $original_filename
660 * @param resource $fp
661 *
662 * @return array
663 */
664 public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
665 try {
666 if (!$this->openfile($filename, $filesize, $fp)) {
667 return $this->info;
668 }
669
670 // Handle tags
671 foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
672 $option_tag = 'option_tag_'.$tag_name;
673 if ($this->$option_tag) {
674 $this->include_module('tag.'.$tag_name);
675 try {
676 $tag_class = 'getid3_'.$tag_name;
677 $tag = new $tag_class($this);
678 $tag->Analyze();
679 }
680 catch (getid3_exception $e) {
681 throw $e;
682 }
683 } else {
684 $this->warning('skipping check for '.$tag_name.' tags since option_tag_'.$tag_name.'=FALSE');
685 }
686 }
687 if (isset($this->info['id3v2']['tag_offset_start'])) {
688 $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
689 }
690 foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
691 if (isset($this->info[$tag_key]['tag_offset_start'])) {
692 $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
693 }
694 }
695
696 // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
697 if (!$this->option_tag_id3v2) {
698 fseek($this->fp, 0);
699 $header = fread($this->fp, 10);
700 if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
701 $this->info['id3v2']['header'] = true;
702 $this->info['id3v2']['majorversion'] = ord($header[3]);
703 $this->info['id3v2']['minorversion'] = ord($header[4]);
704 $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
705 }
706 }
707
708 // read 32 kb file data
709 fseek($this->fp, $this->info['avdataoffset']);
710 $formattest = fread($this->fp, 32774);
711
712 // determine format
713 $determined_format = $this->GetFileFormat($formattest, ($original_filename ? $original_filename : $filename));
714
715 // unable to determine file format
716 if (!$determined_format) {
717 fclose($this->fp);
718 return $this->error('unable to determine file format');
719 }
720
721 // check for illegal ID3 tags
722 if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
723 if ($determined_format['fail_id3'] === 'ERROR') {
724 fclose($this->fp);
725 return $this->error('ID3 tags not allowed on this file type.');
726 } elseif ($determined_format['fail_id3'] === 'WARNING') {
727 $this->warning('ID3 tags not allowed on this file type.');
728 }
729 }
730
731 // check for illegal APE tags
732 if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
733 if ($determined_format['fail_ape'] === 'ERROR') {
734 fclose($this->fp);
735 return $this->error('APE tags not allowed on this file type.');
736 } elseif ($determined_format['fail_ape'] === 'WARNING') {
737 $this->warning('APE tags not allowed on this file type.');
738 }
739 }
740
741 // set mime type
742 $this->info['mime_type'] = $determined_format['mime_type'];
743
744 // supported format signature pattern detected, but module deleted
745 if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
746 fclose($this->fp);
747 return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
748 }
749
750 // module requires mb_convert_encoding/iconv support
751 // Check encoding/iconv support
752 if (!empty($determined_format['iconv_req']) && !function_exists('mb_convert_encoding') && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
753 $errormessage = 'mb_convert_encoding() or 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. ';
754 if (GETID3_OS_ISWINDOWS) {
755 $errormessage .= 'PHP does not have mb_convert_encoding() or iconv() support. Please enable php_mbstring.dll / php_iconv.dll in php.ini, and copy php_mbstring.dll / iconv.dll from c:/php/dlls to c:/windows/system32';
756 } else {
757 $errormessage .= 'PHP is not compiled with mb_convert_encoding() or iconv() support. Please recompile with the --enable-mbstring / --with-iconv switch';
758 }
759 return $this->error($errormessage);
760 }
761
762 // include module
763 include_once(GETID3_INCLUDEPATH.$determined_format['include']);
764
765 // instantiate module class
766 $class_name = 'getid3_'.$determined_format['module'];
767 if (!class_exists($class_name)) {
768 return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
769 }
770 $class = new $class_name($this);
771
772 // set module-specific options
773 foreach (get_object_vars($this) as $getid3_object_vars_key => $getid3_object_vars_value) {
774 if (preg_match('#^options_([^_]+)_([^_]+)_(.+)$#i', $getid3_object_vars_key, $matches)) {
775 list($dummy, $GOVgroup, $GOVmodule, $GOVsetting) = $matches;
776 $GOVgroup = (($GOVgroup == 'audiovideo') ? 'audio-video' : $GOVgroup); // variable names can only contain 0-9a-z_ so standardize here
777 if (($GOVgroup == $determined_format['group']) && ($GOVmodule == $determined_format['module'])) {
778 $class->$GOVsetting = $getid3_object_vars_value;
779 }
780 }
781 }
782
783 $class->Analyze();
784 unset($class);
785
786 // close file
787 fclose($this->fp);
788
789 // process all tags - copy to 'tags' and convert charsets
790 if ($this->option_tags_process) {
791 $this->HandleAllTags();
792 }
793
794 // perform more calculations
795 if ($this->option_extra_info) {
796 $this->ChannelsBitratePlaytimeCalculations();
797 $this->CalculateCompressionRatioVideo();
798 $this->CalculateCompressionRatioAudio();
799 $this->CalculateReplayGain();
800 $this->ProcessAudioStreams();
801 }
802
803 // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
804 if ($this->option_md5_data) {
805 // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
806 if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
807 $this->getHashdata('md5');
808 }
809 }
810
811 // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
812 if ($this->option_sha1_data) {
813 $this->getHashdata('sha1');
814 }
815
816 // remove undesired keys
817 $this->CleanUp();
818
819 } catch (Exception $e) {
820 $this->error('Caught exception: '.$e->getMessage());
821 }
822
823 // return info array
824 return $this->info;
825 }
826
827
828 /**
829 * Error handling.
830 *
831 * @param string $message
832 *
833 * @return array
834 */
835 public function error($message) {
836 $this->CleanUp();
837 if (!isset($this->info['error'])) {
838 $this->info['error'] = array();
839 }
840 $this->info['error'][] = $message;
841 return $this->info;
842 }
843
844
845 /**
846 * Warning handling.
847 *
848 * @param string $message
849 *
850 * @return bool
851 */
852 public function warning($message) {
853 $this->info['warning'][] = $message;
854 return true;
855 }
856
857
858 /**
859 * @return bool
860 */
861 private function CleanUp() {
862
863 // remove possible empty keys
864 $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
865 foreach ($AVpossibleEmptyKeys as $dummy => $key) {
866 if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
867 unset($this->info['audio'][$key]);
868 }
869 if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
870 unset($this->info['video'][$key]);
871 }
872 }
873
874 // remove empty root keys
875 if (!empty($this->info)) {
876 foreach ($this->info as $key => $value) {
877 if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
878 unset($this->info[$key]);
879 }
880 }
881 }
882
883 // remove meaningless entries from unknown-format files
884 if (empty($this->info['fileformat'])) {
885 if (isset($this->info['avdataoffset'])) {
886 unset($this->info['avdataoffset']);
887 }
888 if (isset($this->info['avdataend'])) {
889 unset($this->info['avdataend']);
890 }
891 }
892
893 // remove possible duplicated identical entries
894 if (!empty($this->info['error'])) {
895 $this->info['error'] = array_values(array_unique($this->info['error']));
896 }
897 if (!empty($this->info['warning'])) {
898 $this->info['warning'] = array_values(array_unique($this->info['warning']));
899 }
900
901 // remove "global variable" type keys
902 unset($this->info['php_memory_limit']);
903
904 return true;
905 }
906
907 /**
908 * Return array containing information about all supported formats.
909 *
910 * @return array
911 */
912 public function GetFileFormatArray() {
913 static $format_info = array();
914 if (empty($format_info)) {
915 $format_info = array(
916
917 // Audio formats
918
919 // AC-3 - audio - Dolby AC-3 / Dolby Digital
920 'ac3' => array(
921 'pattern' => '^\\x0B\\x77',
922 'group' => 'audio',
923 'module' => 'ac3',
924 'mime_type' => 'audio/ac3',
925 ),
926
927 // AAC - audio - Advanced Audio Coding (AAC) - ADIF format
928 'adif' => array(
929 'pattern' => '^ADIF',
930 'group' => 'audio',
931 'module' => 'aac',
932 'mime_type' => 'audio/aac',
933 'fail_ape' => 'WARNING',
934 ),
935
936/*
937 // AA - audio - Audible Audiobook
938 'aa' => array(
939 'pattern' => '^.{4}\\x57\\x90\\x75\\x36',
940 'group' => 'audio',
941 'module' => 'aa',
942 'mime_type' => 'audio/audible',
943 ),
944*/
945 // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
946 'adts' => array(
947 'pattern' => '^\\xFF[\\xF0-\\xF1\\xF8-\\xF9]',
948 'group' => 'audio',
949 'module' => 'aac',
950 'mime_type' => 'audio/aac',
951 'fail_ape' => 'WARNING',
952 ),
953
954
955 // AU - audio - NeXT/Sun AUdio (AU)
956 'au' => array(
957 'pattern' => '^\\.snd',
958 'group' => 'audio',
959 'module' => 'au',
960 'mime_type' => 'audio/basic',
961 ),
962
963 // AMR - audio - Adaptive Multi Rate
964 'amr' => array(
965 'pattern' => '^\\x23\\x21AMR\\x0A', // #!AMR[0A]
966 'group' => 'audio',
967 'module' => 'amr',
968 'mime_type' => 'audio/amr',
969 ),
970
971 // AVR - audio - Audio Visual Research
972 'avr' => array(
973 'pattern' => '^2BIT',
974 'group' => 'audio',
975 'module' => 'avr',
976 'mime_type' => 'application/octet-stream',
977 ),
978
979 // BONK - audio - Bonk v0.9+
980 'bonk' => array(
981 'pattern' => '^\\x00(BONK|INFO|META| ID3)',
982 'group' => 'audio',
983 'module' => 'bonk',
984 'mime_type' => 'audio/xmms-bonk',
985 ),
986
987 // DSF - audio - Direct Stream Digital (DSD) Storage Facility files (DSF) - https://en.wikipedia.org/wiki/Direct_Stream_Digital
988 'dsf' => array(
989 'pattern' => '^DSD ', // including trailing space: 44 53 44 20
990 'group' => 'audio',
991 'module' => 'dsf',
992 'mime_type' => 'audio/dsd',
993 ),
994
995 // DSS - audio - Digital Speech Standard
996 'dss' => array(
997 'pattern' => '^[\\x02-\\x08]ds[s2]',
998 'group' => 'audio',
999 'module' => 'dss',
1000 'mime_type' => 'application/octet-stream',
1001 ),
1002
1003 // DSDIFF - audio - Direct Stream Digital Interchange File Format
1004 'dsdiff' => array(
1005 'pattern' => '^FRM8',
1006 'group' => 'audio',
1007 'module' => 'dsdiff',
1008 'mime_type' => 'audio/dsd',
1009 ),
1010
1011 // DTS - audio - Dolby Theatre System
1012 'dts' => array(
1013 'pattern' => '^\\x7F\\xFE\\x80\\x01',
1014 'group' => 'audio',
1015 'module' => 'dts',
1016 'mime_type' => 'audio/dts',
1017 ),
1018
1019 // FLAC - audio - Free Lossless Audio Codec
1020 'flac' => array(
1021 'pattern' => '^fLaC',
1022 'group' => 'audio',
1023 'module' => 'flac',
1024 'mime_type' => 'audio/flac',
1025 ),
1026
1027 // LA - audio - Lossless Audio (LA)
1028 'la' => array(
1029 'pattern' => '^LA0[2-4]',
1030 'group' => 'audio',
1031 'module' => 'la',
1032 'mime_type' => 'application/octet-stream',
1033 ),
1034
1035 // LPAC - audio - Lossless Predictive Audio Compression (LPAC)
1036 'lpac' => array(
1037 'pattern' => '^LPAC',
1038 'group' => 'audio',
1039 'module' => 'lpac',
1040 'mime_type' => 'application/octet-stream',
1041 ),
1042
1043 // MIDI - audio - MIDI (Musical Instrument Digital Interface)
1044 'midi' => array(
1045 'pattern' => '^MThd',
1046 'group' => 'audio',
1047 'module' => 'midi',
1048 'mime_type' => 'audio/midi',
1049 ),
1050
1051 // MAC - audio - Monkey's Audio Compressor
1052 'mac' => array(
1053 'pattern' => '^MAC ',
1054 'group' => 'audio',
1055 'module' => 'monkey',
1056 'mime_type' => 'audio/x-monkeys-audio',
1057 ),
1058
1059
1060 // MOD - audio - MODule (SoundTracker)
1061 'mod' => array(
1062 //'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)', // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
1063 'pattern' => '^.{1080}(M\\.K\\.)',
1064 'group' => 'audio',
1065 'module' => 'mod',
1066 'option' => 'mod',
1067 'mime_type' => 'audio/mod',
1068 ),
1069
1070 // MOD - audio - MODule (Impulse Tracker)
1071 'it' => array(
1072 'pattern' => '^IMPM',
1073 'group' => 'audio',
1074 'module' => 'mod',
1075 //'option' => 'it',
1076 'mime_type' => 'audio/it',
1077 ),
1078
1079 // MOD - audio - MODule (eXtended Module, various sub-formats)
1080 'xm' => array(
1081 'pattern' => '^Extended Module',
1082 'group' => 'audio',
1083 'module' => 'mod',
1084 //'option' => 'xm',
1085 'mime_type' => 'audio/xm',
1086 ),
1087
1088 // MOD - audio - MODule (ScreamTracker)
1089 's3m' => array(
1090 'pattern' => '^.{44}SCRM',
1091 'group' => 'audio',
1092 'module' => 'mod',
1093 //'option' => 's3m',
1094 'mime_type' => 'audio/s3m',
1095 ),
1096
1097 // MPC - audio - Musepack / MPEGplus
1098 'mpc' => array(
1099 'pattern' => '^(MPCK|MP\\+)',
1100 'group' => 'audio',
1101 'module' => 'mpc',
1102 'mime_type' => 'audio/x-musepack',
1103 ),
1104
1105 // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
1106 'mp3' => array(
1107 '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]',
1108 'group' => 'audio',
1109 'module' => 'mp3',
1110 'mime_type' => 'audio/mpeg',
1111 ),
1112
1113 // OFR - audio - OptimFROG
1114 'ofr' => array(
1115 'pattern' => '^(\\*RIFF|OFR)',
1116 'group' => 'audio',
1117 'module' => 'optimfrog',
1118 'mime_type' => 'application/octet-stream',
1119 ),
1120
1121 // RKAU - audio - RKive AUdio compressor
1122 'rkau' => array(
1123 'pattern' => '^RKA',
1124 'group' => 'audio',
1125 'module' => 'rkau',
1126 'mime_type' => 'application/octet-stream',
1127 ),
1128
1129 // SHN - audio - Shorten
1130 'shn' => array(
1131 'pattern' => '^ajkg',
1132 'group' => 'audio',
1133 'module' => 'shorten',
1134 'mime_type' => 'audio/xmms-shn',
1135 'fail_id3' => 'ERROR',
1136 'fail_ape' => 'ERROR',
1137 ),
1138
1139 // TAK - audio - Tom's lossless Audio Kompressor
1140 'tak' => array(
1141 'pattern' => '^tBaK',
1142 'group' => 'audio',
1143 'module' => 'tak',
1144 'mime_type' => 'application/octet-stream',
1145 ),
1146
1147 // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
1148 'tta' => array(
1149 'pattern' => '^TTA', // could also be '^TTA(\\x01|\\x02|\\x03|2|1)'
1150 'group' => 'audio',
1151 'module' => 'tta',
1152 'mime_type' => 'application/octet-stream',
1153 ),
1154
1155 // VOC - audio - Creative Voice (VOC)
1156 'voc' => array(
1157 'pattern' => '^Creative Voice File',
1158 'group' => 'audio',
1159 'module' => 'voc',
1160 'mime_type' => 'audio/voc',
1161 ),
1162
1163 // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
1164 'vqf' => array(
1165 'pattern' => '^TWIN',
1166 'group' => 'audio',
1167 'module' => 'vqf',
1168 'mime_type' => 'application/octet-stream',
1169 ),
1170
1171 // WV - audio - WavPack (v4.0+)
1172 'wv' => array(
1173 'pattern' => '^wvpk',
1174 'group' => 'audio',
1175 'module' => 'wavpack',
1176 'mime_type' => 'application/octet-stream',
1177 ),
1178
1179
1180 // Audio-Video formats
1181
1182 // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
1183 'asf' => array(
1184 'pattern' => '^\\x30\\x26\\xB2\\x75\\x8E\\x66\\xCF\\x11\\xA6\\xD9\\x00\\xAA\\x00\\x62\\xCE\\x6C',
1185 'group' => 'audio-video',
1186 'module' => 'asf',
1187 'mime_type' => 'video/x-ms-asf',
1188 'iconv_req' => false,
1189 ),
1190
1191 // BINK - audio/video - Bink / Smacker
1192 'bink' => array(
1193 'pattern' => '^(BIK|SMK)',
1194 'group' => 'audio-video',
1195 'module' => 'bink',
1196 'mime_type' => 'application/octet-stream',
1197 ),
1198
1199 // FLV - audio/video - FLash Video
1200 'flv' => array(
1201 'pattern' => '^FLV[\\x01]',
1202 'group' => 'audio-video',
1203 'module' => 'flv',
1204 'mime_type' => 'video/x-flv',
1205 ),
1206
1207 // IVF - audio/video - IVF
1208 'ivf' => array(
1209 'pattern' => '^DKIF',
1210 'group' => 'audio-video',
1211 'module' => 'ivf',
1212 'mime_type' => 'video/x-ivf',
1213 ),
1214
1215 // MKAV - audio/video - Mastroka
1216 'matroska' => array(
1217 'pattern' => '^\\x1A\\x45\\xDF\\xA3',
1218 'group' => 'audio-video',
1219 'module' => 'matroska',
1220 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
1221 ),
1222
1223 // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
1224 'mpeg' => array(
1225 'pattern' => '^\\x00\\x00\\x01[\\xB3\\xBA]',
1226 'group' => 'audio-video',
1227 'module' => 'mpeg',
1228 'mime_type' => 'video/mpeg',
1229 ),
1230
1231 // NSV - audio/video - Nullsoft Streaming Video (NSV)
1232 'nsv' => array(
1233 'pattern' => '^NSV[sf]',
1234 'group' => 'audio-video',
1235 'module' => 'nsv',
1236 'mime_type' => 'application/octet-stream',
1237 ),
1238
1239 // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
1240 'ogg' => array(
1241 'pattern' => '^OggS',
1242 'group' => 'audio',
1243 'module' => 'ogg',
1244 'mime_type' => 'application/ogg',
1245 'fail_id3' => 'WARNING',
1246 'fail_ape' => 'WARNING',
1247 ),
1248
1249 // QT - audio/video - Quicktime
1250 'quicktime' => array(
1251 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
1252 'group' => 'audio-video',
1253 'module' => 'quicktime',
1254 'mime_type' => 'video/quicktime',
1255 ),
1256
1257 // 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)
1258 'riff' => array(
1259 'pattern' => '^(RIFF|SDSS|FORM)',
1260 'group' => 'audio-video',
1261 'module' => 'riff',
1262 'mime_type' => 'audio/wav',
1263 'fail_ape' => 'WARNING',
1264 ),
1265
1266 // Real - audio/video - RealAudio, RealVideo
1267 'real' => array(
1268 'pattern' => '^\\.(RMF|ra)',
1269 'group' => 'audio-video',
1270 'module' => 'real',
1271 'mime_type' => 'audio/x-realaudio',
1272 ),
1273
1274 // SWF - audio/video - ShockWave Flash
1275 'swf' => array(
1276 'pattern' => '^(F|C)WS',
1277 'group' => 'audio-video',
1278 'module' => 'swf',
1279 'mime_type' => 'application/x-shockwave-flash',
1280 ),
1281
1282 // TS - audio/video - MPEG-2 Transport Stream
1283 'ts' => array(
1284 'pattern' => '^(\\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
1285 'group' => 'audio-video',
1286 'module' => 'ts',
1287 'mime_type' => 'video/MP2T',
1288 ),
1289
1290 // WTV - audio/video - Windows Recorded TV Show
1291 'wtv' => array(
1292 'pattern' => '^\\xB7\\xD8\\x00\\x20\\x37\\x49\\xDA\\x11\\xA6\\x4E\\x00\\x07\\xE9\\x5E\\xAD\\x8D',
1293 'group' => 'audio-video',
1294 'module' => 'wtv',
1295 'mime_type' => 'video/x-ms-wtv',
1296 ),
1297
1298
1299 // Still-Image formats
1300
1301 // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
1302 'bmp' => array(
1303 'pattern' => '^BM',
1304 'group' => 'graphic',
1305 'module' => 'bmp',
1306 'mime_type' => 'image/bmp',
1307 'fail_id3' => 'ERROR',
1308 'fail_ape' => 'ERROR',
1309 ),
1310
1311 // GIF - still image - Graphics Interchange Format
1312 'gif' => array(
1313 'pattern' => '^GIF',
1314 'group' => 'graphic',
1315 'module' => 'gif',
1316 'mime_type' => 'image/gif',
1317 'fail_id3' => 'ERROR',
1318 'fail_ape' => 'ERROR',
1319 ),
1320
1321 // JPEG - still image - Joint Photographic Experts Group (JPEG)
1322 'jpg' => array(
1323 'pattern' => '^\\xFF\\xD8\\xFF',
1324 'group' => 'graphic',
1325 'module' => 'jpg',
1326 'mime_type' => 'image/jpeg',
1327 'fail_id3' => 'ERROR',
1328 'fail_ape' => 'ERROR',
1329 ),
1330
1331 // PCD - still image - Kodak Photo CD
1332 'pcd' => array(
1333 'pattern' => '^.{2048}PCD_IPI\\x00',
1334 'group' => 'graphic',
1335 'module' => 'pcd',
1336 'mime_type' => 'image/x-photo-cd',
1337 'fail_id3' => 'ERROR',
1338 'fail_ape' => 'ERROR',
1339 ),
1340
1341
1342 // PNG - still image - Portable Network Graphics (PNG)
1343 'png' => array(
1344 'pattern' => '^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A',
1345 'group' => 'graphic',
1346 'module' => 'png',
1347 'mime_type' => 'image/png',
1348 'fail_id3' => 'ERROR',
1349 'fail_ape' => 'ERROR',
1350 ),
1351
1352
1353 // SVG - still image - Scalable Vector Graphics (SVG)
1354 'svg' => array(
1355 'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http://www\\.w3\\.org/2000/svg")',
1356 'group' => 'graphic',
1357 'module' => 'svg',
1358 'mime_type' => 'image/svg+xml',
1359 'fail_id3' => 'ERROR',
1360 'fail_ape' => 'ERROR',
1361 ),
1362
1363
1364 // TIFF - still image - Tagged Information File Format (TIFF)
1365 'tiff' => array(
1366 'pattern' => '^(II\\x2A\\x00|MM\\x00\\x2A)',
1367 'group' => 'graphic',
1368 'module' => 'tiff',
1369 'mime_type' => 'image/tiff',
1370 'fail_id3' => 'ERROR',
1371 'fail_ape' => 'ERROR',
1372 ),
1373
1374
1375 // EFAX - still image - eFax (TIFF derivative)
1376 'efax' => array(
1377 'pattern' => '^\\xDC\\xFE',
1378 'group' => 'graphic',
1379 'module' => 'efax',
1380 'mime_type' => 'image/efax',
1381 'fail_id3' => 'ERROR',
1382 'fail_ape' => 'ERROR',
1383 ),
1384
1385
1386 // Data formats
1387
1388 // ISO - data - International Standards Organization (ISO) CD-ROM Image
1389 'iso' => array(
1390 'pattern' => '^.{32769}CD001',
1391 'group' => 'misc',
1392 'module' => 'iso',
1393 'mime_type' => 'application/octet-stream',
1394 'fail_id3' => 'ERROR',
1395 'fail_ape' => 'ERROR',
1396 'iconv_req' => false,
1397 ),
1398
1399 // HPK - data - HPK compressed data
1400 'hpk' => array(
1401 'pattern' => '^BPUL',
1402 'group' => 'archive',
1403 'module' => 'hpk',
1404 'mime_type' => 'application/octet-stream',
1405 'fail_id3' => 'ERROR',
1406 'fail_ape' => 'ERROR',
1407 ),
1408
1409 // RAR - data - RAR compressed data
1410 'rar' => array(
1411 'pattern' => '^Rar\\!',
1412 'group' => 'archive',
1413 'module' => 'rar',
1414 'mime_type' => 'application/vnd.rar',
1415 'fail_id3' => 'ERROR',
1416 'fail_ape' => 'ERROR',
1417 ),
1418
1419 // SZIP - audio/data - SZIP compressed data
1420 'szip' => array(
1421 'pattern' => '^SZ\\x0A\\x04',
1422 'group' => 'archive',
1423 'module' => 'szip',
1424 'mime_type' => 'application/octet-stream',
1425 'fail_id3' => 'ERROR',
1426 'fail_ape' => 'ERROR',
1427 ),
1428
1429 // TAR - data - TAR compressed data
1430 'tar' => array(
1431 '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}',
1432 'group' => 'archive',
1433 'module' => 'tar',
1434 'mime_type' => 'application/x-tar',
1435 'fail_id3' => 'ERROR',
1436 'fail_ape' => 'ERROR',
1437 ),
1438
1439 // GZIP - data - GZIP compressed data
1440 'gz' => array(
1441 'pattern' => '^\\x1F\\x8B\\x08',
1442 'group' => 'archive',
1443 'module' => 'gzip',
1444 'mime_type' => 'application/gzip',
1445 'fail_id3' => 'ERROR',
1446 'fail_ape' => 'ERROR',
1447 ),
1448
1449 // ZIP - data - ZIP compressed data
1450 'zip' => array(
1451 'pattern' => '^PK\\x03\\x04',
1452 'group' => 'archive',
1453 'module' => 'zip',
1454 'mime_type' => 'application/zip',
1455 'fail_id3' => 'ERROR',
1456 'fail_ape' => 'ERROR',
1457 ),
1458
1459 // XZ - data - XZ compressed data
1460 'xz' => array(
1461 'pattern' => '^\\xFD7zXZ\\x00',
1462 'group' => 'archive',
1463 'module' => 'xz',
1464 'mime_type' => 'application/x-xz',
1465 'fail_id3' => 'ERROR',
1466 'fail_ape' => 'ERROR',
1467 ),
1468
1469 // XZ - data - XZ compressed data
1470 '7zip' => array(
1471 'pattern' => '^7z\\xBC\\xAF\\x27\\x1C',
1472 'group' => 'archive',
1473 'module' => '7zip',
1474 'mime_type' => 'application/x-7z-compressed',
1475 'fail_id3' => 'ERROR',
1476 'fail_ape' => 'ERROR',
1477 ),
1478
1479
1480 // Misc other formats
1481
1482 // GPX - data - GPS Exchange Format
1483 'gpx' => array (
1484 'pattern' => '^<\\?xml [^>]+>[\s]*<gpx ',
1485 'group' => 'misc',
1486 'module' => 'gpx',
1487 'mime_type' => 'application/gpx+xml',
1488 'fail_id3' => 'ERROR',
1489 'fail_ape' => 'ERROR',
1490 ),
1491
1492 // PAR2 - data - Parity Volume Set Specification 2.0
1493 'par2' => array (
1494 'pattern' => '^PAR2\\x00PKT',
1495 'group' => 'misc',
1496 'module' => 'par2',
1497 'mime_type' => 'application/octet-stream',
1498 'fail_id3' => 'ERROR',
1499 'fail_ape' => 'ERROR',
1500 ),
1501
1502 // PDF - data - Portable Document Format
1503 'pdf' => array(
1504 'pattern' => '^\\x25PDF',
1505 'group' => 'misc',
1506 'module' => 'pdf',
1507 'mime_type' => 'application/pdf',
1508 'fail_id3' => 'ERROR',
1509 'fail_ape' => 'ERROR',
1510 ),
1511
1512 // MSOFFICE - data - ZIP compressed data
1513 'msoffice' => array(
1514 'pattern' => '^\\xD0\\xCF\\x11\\xE0\\xA1\\xB1\\x1A\\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
1515 'group' => 'misc',
1516 'module' => 'msoffice',
1517 'mime_type' => 'application/octet-stream',
1518 'fail_id3' => 'ERROR',
1519 'fail_ape' => 'ERROR',
1520 ),
1521
1522 // TORRENT - .torrent
1523 'torrent' => array(
1524 'pattern' => '^(d8\\:announce|d7\\:comment)',
1525 'group' => 'misc',
1526 'module' => 'torrent',
1527 'mime_type' => 'application/x-bittorrent',
1528 'fail_id3' => 'ERROR',
1529 'fail_ape' => 'ERROR',
1530 ),
1531
1532 // CUE - data - CUEsheet (index to single-file disc images)
1533 'cue' => array(
1534 'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
1535 'group' => 'misc',
1536 'module' => 'cue',
1537 'mime_type' => 'application/octet-stream',
1538 ),
1539
1540 );
1541 }
1542
1543 return $format_info;
1544 }
1545
1546 /**
1547 * @param string $filedata
1548 * @param string $filename
1549 *
1550 * @return mixed|false
1551 */
1552 public function GetFileFormat(&$filedata, $filename='') {
1553 // this function will determine the format of a file based on usually
1554 // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
1555 // and in the case of ISO CD image, 6 bytes offset 32kb from the start
1556 // of the file).
1557
1558 // Identify file format - loop through $format_info and detect with reg expr
1559 foreach ($this->GetFileFormatArray() as $format_name => $info) {
1560 // The /s switch on preg_match() forces preg_match() NOT to treat
1561 // newline (0x0A) characters as special chars but do a binary match
1562 if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
1563 $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1564 return $info;
1565 }
1566 }
1567
1568
1569 if (preg_match('#\\.mp[123a]$#i', $filename)) {
1570 // Too many mp3 encoders on the market put garbage in front of mpeg files
1571 // use assume format on these if format detection failed
1572 $GetFileFormatArray = $this->GetFileFormatArray();
1573 $info = $GetFileFormatArray['mp3'];
1574 $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1575 return $info;
1576 } elseif (preg_match('#\\.mp[cp\\+]$#i', $filename) && preg_match('#[\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]#s', $filedata)) {
1577 // old-format (SV4-SV6) Musepack header that has a very loose pattern match and could falsely match other data (e.g. corrupt mp3)
1578 // only enable this pattern check if the filename ends in .mpc/mpp/mp+
1579 $GetFileFormatArray = $this->GetFileFormatArray();
1580 $info = $GetFileFormatArray['mpc'];
1581 $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1582 return $info;
1583 } elseif (preg_match('#\\.cue$#i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
1584 // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
1585 // so until I think of something better, just go by filename if all other format checks fail
1586 // and verify there's at least one instance of "TRACK xx AUDIO" in the file
1587 $GetFileFormatArray = $this->GetFileFormatArray();
1588 $info = $GetFileFormatArray['cue'];
1589 $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1590 return $info;
1591 }
1592
1593 return false;
1594 }
1595
1596 /**
1597 * Converts array to $encoding charset from $this->encoding.
1598 *
1599 * @param array $array
1600 * @param string $encoding
1601 */
1602 public function CharConvert(&$array, $encoding) {
1603
1604 // identical encoding - end here
1605 if ($encoding == $this->encoding) {
1606 return;
1607 }
1608
1609 // loop thru array
1610 foreach ($array as $key => $value) {
1611
1612 // go recursive
1613 if (is_array($value)) {
1614 $this->CharConvert($array[$key], $encoding);
1615 }
1616
1617 // convert string
1618 elseif (is_string($value)) {
1619 $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
1620 }
1621 }
1622 }
1623
1624 /**
1625 * @return bool
1626 */
1627 public function HandleAllTags() {
1628
1629 // key name => array (tag name, character encoding)
1630 static $tags;
1631 if (empty($tags)) {
1632 $tags = array(
1633 'asf' => array('asf' , 'UTF-16LE'),
1634 'midi' => array('midi' , 'ISO-8859-1'),
1635 'nsv' => array('nsv' , 'ISO-8859-1'),
1636 'ogg' => array('vorbiscomment' , 'UTF-8'),
1637 'png' => array('png' , 'UTF-8'),
1638 'tiff' => array('tiff' , 'ISO-8859-1'),
1639 'quicktime' => array('quicktime' , 'UTF-8'),
1640 'real' => array('real' , 'ISO-8859-1'),
1641 'vqf' => array('vqf' , 'ISO-8859-1'),
1642 'zip' => array('zip' , 'ISO-8859-1'),
1643 'riff' => array('riff' , 'ISO-8859-1'),
1644 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
1645 'id3v1' => array('id3v1' , $this->encoding_id3v1),
1646 '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
1647 'ape' => array('ape' , 'UTF-8'),
1648 'cue' => array('cue' , 'ISO-8859-1'),
1649 'matroska' => array('matroska' , 'UTF-8'),
1650 'flac' => array('vorbiscomment' , 'UTF-8'),
1651 'divxtag' => array('divx' , 'ISO-8859-1'),
1652 'iptc' => array('iptc' , 'ISO-8859-1'),
1653 'dsdiff' => array('dsdiff' , 'ISO-8859-1'),
1654 );
1655 }
1656
1657 // loop through comments array
1658 foreach ($tags as $comment_name => $tagname_encoding_array) {
1659 list($tag_name, $encoding) = $tagname_encoding_array;
1660
1661 // fill in default encoding type if not already present
1662 if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1663 $this->info[$comment_name]['encoding'] = $encoding;
1664 }
1665
1666 // copy comments if key name set
1667 if (!empty($this->info[$comment_name]['comments'])) {
1668 foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1669 foreach ($valuearray as $key => $value) {
1670 if (is_string($value)) {
1671 $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
1672 }
1673 if (isset($value) && $value !== "") {
1674 if (!is_numeric($key)) {
1675 $this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value;
1676 } else {
1677 $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
1678 }
1679 }
1680 }
1681 if ($tag_key == 'picture') {
1682 // pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
1683 unset($this->info[$comment_name]['comments'][$tag_key]);
1684 }
1685 }
1686
1687 if (!isset($this->info['tags'][$tag_name])) {
1688 // comments are set but contain nothing but empty strings, so skip
1689 continue;
1690 }
1691
1692 $this->CharConvert($this->info['tags'][$tag_name], $this->info[$comment_name]['encoding']); // only copy gets converted!
1693
1694 if ($this->option_tags_html) {
1695 foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1696 if ($tag_key == 'picture') {
1697 // Do not to try to convert binary picture data to HTML
1698 // https://github.com/JamesHeinrich/getID3/issues/178
1699 continue;
1700 }
1701 $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
1702 }
1703 }
1704
1705 }
1706
1707 }
1708
1709 // pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
1710 if (!empty($this->info['tags'])) {
1711 $unset_keys = array('tags', 'tags_html');
1712 foreach ($this->info['tags'] as $tagtype => $tagarray) {
1713 foreach ($tagarray as $tagname => $tagdata) {
1714 if ($tagname == 'picture') {
1715 foreach ($tagdata as $key => $tagarray) {
1716 $this->info['comments']['picture'][] = $tagarray;
1717 if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
1718 if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
1719 unset($this->info['tags'][$tagtype][$tagname][$key]);
1720 }
1721 if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
1722 unset($this->info['tags_html'][$tagtype][$tagname][$key]);
1723 }
1724 }
1725 }
1726 }
1727 }
1728 foreach ($unset_keys as $unset_key) {
1729 // remove possible empty keys from (e.g. [tags][id3v2][picture])
1730 if (empty($this->info[$unset_key][$tagtype]['picture'])) {
1731 unset($this->info[$unset_key][$tagtype]['picture']);
1732 }
1733 if (empty($this->info[$unset_key][$tagtype])) {
1734 unset($this->info[$unset_key][$tagtype]);
1735 }
1736 if (empty($this->info[$unset_key])) {
1737 unset($this->info[$unset_key]);
1738 }
1739 }
1740 // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture])
1741 if (isset($this->info[$tagtype]['comments']['picture'])) {
1742 unset($this->info[$tagtype]['comments']['picture']);
1743 }
1744 if (empty($this->info[$tagtype]['comments'])) {
1745 unset($this->info[$tagtype]['comments']);
1746 }
1747 if (empty($this->info[$tagtype])) {
1748 unset($this->info[$tagtype]);
1749 }
1750 }
1751 }
1752 return true;
1753 }
1754
1755 /**
1756 * Calls getid3_lib::CopyTagsToComments() but passes in the option_tags_html setting from this instance of getID3
1757 *
1758 * @param array $ThisFileInfo
1759 *
1760 * @return bool
1761 */
1762 public function CopyTagsToComments(&$ThisFileInfo) {
1763 return getid3_lib::CopyTagsToComments($ThisFileInfo, $this->option_tags_html);
1764 }
1765
1766 /**
1767 * @param string $algorithm
1768 *
1769 * @return array|bool
1770 */
1771 public function getHashdata($algorithm) {
1772 switch ($algorithm) {
1773 case 'md5':
1774 case 'sha1':
1775 break;
1776
1777 default:
1778 return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1779 }
1780
1781 if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
1782
1783 // We cannot get an identical md5_data value for Ogg files where the comments
1784 // span more than 1 Ogg page (compared to the same audio data with smaller
1785 // comments) using the normal getID3() method of MD5'ing the data between the
1786 // end of the comments and the end of the file (minus any trailing tags),
1787 // because the page sequence numbers of the pages that the audio data is on
1788 // do not match. Under normal circumstances, where comments are smaller than
1789 // the nominal 4-8kB page size, then this is not a problem, but if there are
1790 // very large comments, the only way around it is to strip off the comment
1791 // tags with vorbiscomment and MD5 that file.
1792 // This procedure must be applied to ALL Ogg files, not just the ones with
1793 // comments larger than 1 page, because the below method simply MD5's the
1794 // whole file with the comments stripped, not just the portion after the
1795 // comments block (which is the standard getID3() method.
1796
1797 // The above-mentioned problem of comments spanning multiple pages and changing
1798 // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1799 // currently vorbiscomment only works on OggVorbis files.
1800
1801 if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
1802
1803 $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
1804 $this->info[$algorithm.'_data'] = false;
1805
1806 } else {
1807
1808 // Prevent user from aborting script
1809 $old_abort = ignore_user_abort(true);
1810
1811 // Create empty file
1812 $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
1813 touch($empty);
1814
1815 // Use vorbiscomment to make temp file without comments
1816 $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
1817 $file = $this->info['filenamepath'];
1818
1819 if (GETID3_OS_ISWINDOWS) {
1820
1821 if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1822
1823 $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1824 $VorbisCommentError = shell_exec($commandline);
1825
1826 } else {
1827
1828 $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1829
1830 }
1831
1832 } else {
1833
1834 $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1835 $VorbisCommentError = shell_exec($commandline);
1836
1837 }
1838
1839 if (!empty($VorbisCommentError)) {
1840
1841 $this->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);
1842 $this->info[$algorithm.'_data'] = false;
1843
1844 } else {
1845
1846 // Get hash of newly created file
1847 switch ($algorithm) {
1848 case 'md5':
1849 $this->info[$algorithm.'_data'] = md5_file($temp);
1850 break;
1851
1852 case 'sha1':
1853 $this->info[$algorithm.'_data'] = sha1_file($temp);
1854 break;
1855 }
1856 }
1857
1858 // Clean up
1859 unlink($empty);
1860 unlink($temp);
1861
1862 // Reset abort setting
1863 ignore_user_abort($old_abort);
1864
1865 }
1866
1867 } else {
1868
1869 if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1870
1871 // get hash from part of file
1872 $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1873
1874 } else {
1875
1876 // get hash from whole file
1877 switch ($algorithm) {
1878 case 'md5':
1879 $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
1880 break;
1881
1882 case 'sha1':
1883 $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
1884 break;
1885 }
1886 }
1887
1888 }
1889 return true;
1890 }
1891
1892 public function ChannelsBitratePlaytimeCalculations() {
1893
1894 // set channelmode on audio
1895 if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
1896 // ignore
1897 } elseif ($this->info['audio']['channels'] == 1) {
1898 $this->info['audio']['channelmode'] = 'mono';
1899 } elseif ($this->info['audio']['channels'] == 2) {
1900 $this->info['audio']['channelmode'] = 'stereo';
1901 }
1902
1903 // Calculate combined bitrate - audio + video
1904 $CombinedBitrate = 0;
1905 $CombinedBitrate += (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] != 'free') ? $this->info['audio']['bitrate'] : 0);
1906 $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1907 if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1908 $this->info['bitrate'] = $CombinedBitrate;
1909 }
1910 //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1911 // // for example, VBR MPEG video files cannot determine video bitrate:
1912 // // should not set overall bitrate and playtime from audio bitrate only
1913 // unset($this->info['bitrate']);
1914 //}
1915
1916 // video bitrate undetermined, but calculable
1917 if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1918 // if video bitrate not set
1919 if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1920 // AND if audio bitrate is set to same as overall bitrate
1921 if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1922 // AND if playtime is set
1923 if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1924 // AND if AV data offset start/end is known
1925 // THEN we can calculate the video bitrate
1926 $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1927 $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1928 }
1929 }
1930 }
1931 }
1932
1933 if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
1934 $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1935 }
1936
1937 if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
1938 $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
1939 }
1940 if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
1941 if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
1942 // audio only
1943 $this->info['audio']['bitrate'] = $this->info['bitrate'];
1944 } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
1945 // video only
1946 $this->info['video']['bitrate'] = $this->info['bitrate'];
1947 }
1948 }
1949
1950 // Set playtime string
1951 if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1952 $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1953 }
1954 }
1955
1956 /**
1957 * @return bool
1958 */
1959 public function CalculateCompressionRatioVideo() {
1960 if (empty($this->info['video'])) {
1961 return false;
1962 }
1963 if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1964 return false;
1965 }
1966 if (empty($this->info['video']['bits_per_sample'])) {
1967 return false;
1968 }
1969
1970 switch ($this->info['video']['dataformat']) {
1971 case 'bmp':
1972 case 'gif':
1973 case 'jpeg':
1974 case 'jpg':
1975 case 'png':
1976 case 'tiff':
1977 $FrameRate = 1;
1978 $PlaytimeSeconds = 1;
1979 $BitrateCompressed = $this->info['filesize'] * 8;
1980 break;
1981
1982 default:
1983 if (!empty($this->info['video']['frame_rate'])) {
1984 $FrameRate = $this->info['video']['frame_rate'];
1985 } else {
1986 return false;
1987 }
1988 if (!empty($this->info['playtime_seconds'])) {
1989 $PlaytimeSeconds = $this->info['playtime_seconds'];
1990 } else {
1991 return false;
1992 }
1993 if (!empty($this->info['video']['bitrate'])) {
1994 $BitrateCompressed = $this->info['video']['bitrate'];
1995 } else {
1996 return false;
1997 }
1998 break;
1999 }
2000 $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
2001
2002 $this->info['video']['compression_ratio'] = getid3_lib::SafeDiv($BitrateCompressed, $BitrateUncompressed, 1);
2003 return true;
2004 }
2005
2006 /**
2007 * @return bool
2008 */
2009 public function CalculateCompressionRatioAudio() {
2010 if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
2011 return false;
2012 }
2013 if ($this->info['audio']['bitrate'] != 'free') {
2014 $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));
2015 }
2016
2017 if (!empty($this->info['audio']['streams'])) {
2018 foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
2019 if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
2020 $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
2021 }
2022 }
2023 }
2024 return true;
2025 }
2026
2027 /**
2028 * @return bool
2029 */
2030 public function CalculateReplayGain() {
2031 if (isset($this->info['replay_gain'])) {
2032 if (!isset($this->info['replay_gain']['reference_volume'])) {
2033 $this->info['replay_gain']['reference_volume'] = 89.0;
2034 }
2035 if (isset($this->info['replay_gain']['track']['adjustment'])) {
2036 $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
2037 }
2038 if (isset($this->info['replay_gain']['album']['adjustment'])) {
2039 $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
2040 }
2041
2042 if (isset($this->info['replay_gain']['track']['peak'])) {
2043 $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
2044 }
2045 if (isset($this->info['replay_gain']['album']['peak'])) {
2046 $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
2047 }
2048 }
2049 return true;
2050 }
2051
2052 /**
2053 * @return bool
2054 */
2055 public function ProcessAudioStreams() {
2056 if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
2057 if (!isset($this->info['audio']['streams'])) {
2058 foreach ($this->info['audio'] as $key => $value) {
2059 if ($key != 'streams') {
2060 $this->info['audio']['streams'][0][$key] = $value;
2061 }
2062 }
2063 }
2064 }
2065 return true;
2066 }
2067
2068 /**
2069 * @return string|bool
2070 */
2071 public function getid3_tempnam() {
2072 return tempnam($this->tempdir, 'gI3');
2073 }
2074
2075 /**
2076 * @param string $name
2077 *
2078 * @return bool
2079 *
2080 * @throws getid3_exception
2081 */
2082 public function include_module($name) {
2083 //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
2084 if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
2085 throw new getid3_exception('Required module.'.$name.'.php is missing.');
2086 }
2087 include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
2088 return true;
2089 }
2090
2091 /**
2092 * @param string $filename
2093 *
2094 * @return bool
2095 */
2096 public static function is_writable ($filename) {
2097 $ret = is_writable($filename);
2098 if (!$ret) {
2099 $perms = fileperms($filename);
2100 $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
2101 }
2102 return $ret;
2103 }
2104
2105}
2106
2107
2108abstract class getid3_handler
2109{
2110
2111 /**
2112 * @var getID3
2113 */
2114 protected $getid3; // pointer
2115
2116 /**
2117 * Analyzing filepointer or string.
2118 *
2119 * @var bool
2120 */
2121 protected $data_string_flag = false;
2122
2123 /**
2124 * String to analyze.
2125 *
2126 * @var string
2127 */
2128 protected $data_string = '';
2129
2130 /**
2131 * Seek position in string.
2132 *
2133 * @var int
2134 */
2135 protected $data_string_position = 0;
2136
2137 /**
2138 * String length.
2139 *
2140 * @var int
2141 */
2142 protected $data_string_length = 0;
2143
2144 /**
2145 * @var string
2146 */
2147 private $dependency_to;
2148
2149 /**
2150 * getid3_handler constructor.
2151 *
2152 * @param getID3 $getid3
2153 * @param string $call_module
2154 */
2155 public function __construct(getID3 $getid3, $call_module=null) {
2156 $this->getid3 = $getid3;
2157
2158 if ($call_module) {
2159 $this->dependency_to = str_replace('getid3_', '', $call_module);
2160 }
2161 }
2162
2163 /**
2164 * Analyze from file pointer.
2165 *
2166 * @return bool
2167 */
2168 abstract public function Analyze();
2169
2170 /**
2171 * Analyze from string instead.
2172 *
2173 * @param string $string
2174 */
2175 public function AnalyzeString($string) {
2176 // Enter string mode
2177 $this->setStringMode($string);
2178
2179 // Save info
2180 $saved_avdataoffset = $this->getid3->info['avdataoffset'];
2181 $saved_avdataend = $this->getid3->info['avdataend'];
2182 $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call
2183
2184 // Reset some info
2185 $this->getid3->info['avdataoffset'] = 0;
2186 $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length;
2187
2188 // Analyze
2189 $this->Analyze();
2190
2191 // Restore some info
2192 $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
2193 $this->getid3->info['avdataend'] = $saved_avdataend;
2194 $this->getid3->info['filesize'] = $saved_filesize;
2195
2196 // Exit string mode
2197 $this->data_string_flag = false;
2198 }
2199
2200 /**
2201 * @param string $string
2202 */
2203 public function setStringMode($string) {
2204 $this->data_string_flag = true;
2205 $this->data_string = $string;
2206 $this->data_string_length = strlen($string);
2207 }
2208
2209 /**
2210 * @phpstan-impure
2211 *
2212 * @return int|bool
2213 */
2214 protected function ftell() {
2215 if ($this->data_string_flag) {
2216 return $this->data_string_position;
2217 }
2218 return ftell($this->getid3->fp);
2219 }
2220
2221 /**
2222 * @param int $bytes
2223 *
2224 * @phpstan-impure
2225 *
2226 * @return string|false
2227 *
2228 * @throws getid3_exception
2229 */
2230 protected function fread($bytes) {
2231 if ($this->data_string_flag) {
2232 $this->data_string_position += $bytes;
2233 return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
2234 }
2235 if ($bytes == 0) {
2236 return '';
2237 } elseif ($bytes < 0) {
2238 throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().')', 10);
2239 }
2240 $pos = $this->ftell() + $bytes;
2241 if (!getid3_lib::intValueSupported($pos)) {
2242 throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
2243 }
2244
2245 //return fread($this->getid3->fp, $bytes);
2246 /*
2247 * https://www.getid3.org/phpBB3/viewtopic.php?t=1930
2248 * "I found out that the root cause for the problem was how getID3 uses the PHP system function fread().
2249 * It seems to assume that fread() would always return as many bytes as were requested.
2250 * However, according the PHP manual (http://php.net/manual/en/function.fread.php), this is the case only with regular local files, but not e.g. with Linux pipes.
2251 * The call may return only part of the requested data and a new call is needed to get more."
2252 */
2253 $contents = '';
2254 do {
2255 //if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
2256 if (($this->getid3->memory_limit > 0) && (($bytes / $this->getid3->memory_limit) > 0.99)) { // enable a more-fuzzy match to prevent close misses generating errors like "PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33554464 bytes)"
2257 throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10);
2258 }
2259 $part = fread($this->getid3->fp, $bytes);
2260 $partLength = strlen($part);
2261 $bytes -= $partLength;
2262 $contents .= $part;
2263 } while (($bytes > 0) && ($partLength > 0));
2264 return $contents;
2265 }
2266
2267 /**
2268 * @param int $bytes
2269 * @param int $whence
2270 *
2271 * @phpstan-impure
2272 *
2273 * @return int
2274 *
2275 * @throws getid3_exception
2276 */
2277 protected function fseek($bytes, $whence=SEEK_SET) {
2278 if ($this->data_string_flag) {
2279 switch ($whence) {
2280 case SEEK_SET:
2281 $this->data_string_position = $bytes;
2282 break;
2283
2284 case SEEK_CUR:
2285 $this->data_string_position += $bytes;
2286 break;
2287
2288 case SEEK_END:
2289 $this->data_string_position = $this->data_string_length + $bytes;
2290 break;
2291 }
2292 return 0; // fseek returns 0 on success
2293 }
2294
2295 $pos = $bytes;
2296 if ($whence == SEEK_CUR) {
2297 $pos = $this->ftell() + $bytes;
2298 } elseif ($whence == SEEK_END) {
2299 $pos = $this->getid3->info['filesize'] + $bytes;
2300 }
2301 if (!getid3_lib::intValueSupported($pos)) {
2302 throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
2303 }
2304
2305 // https://github.com/JamesHeinrich/getID3/issues/327
2306 $result = fseek($this->getid3->fp, $bytes, $whence);
2307 if ($result !== 0) { // fseek returns 0 on success
2308 throw new getid3_exception('cannot fseek('.$pos.'). resource/stream does not appear to support seeking', 10);
2309 }
2310 return $result;
2311 }
2312
2313 /**
2314 * @phpstan-impure
2315 *
2316 * @return string|false
2317 *
2318 * @throws getid3_exception
2319 */
2320 protected function fgets() {
2321 // must be able to handle CR/LF/CRLF but not read more than one lineend
2322 $buffer = ''; // final string we will return
2323 $prevchar = ''; // save previously-read character for end-of-line checking
2324 if ($this->data_string_flag) {
2325 while (true) {
2326 $thischar = substr($this->data_string, $this->data_string_position++, 1);
2327 if (($prevchar == "\r") && ($thischar != "\n")) {
2328 // read one byte too many, back up
2329 $this->data_string_position--;
2330 break;
2331 }
2332 $buffer .= $thischar;
2333 if ($thischar == "\n") {
2334 break;
2335 }
2336 if ($this->data_string_position >= $this->data_string_length) {
2337 // EOF
2338 break;
2339 }
2340 $prevchar = $thischar;
2341 }
2342
2343 } else {
2344
2345 // Ideally we would just use PHP's fgets() function, however...
2346 // it does not behave consistently with regards to mixed line endings, may be system-dependent
2347 // and breaks entirely when given a file with mixed \r vs \n vs \r\n line endings (e.g. some PDFs)
2348 //return fgets($this->getid3->fp);
2349 while (true) {
2350 $thischar = fgetc($this->getid3->fp);
2351 if (($prevchar == "\r") && ($thischar != "\n")) {
2352 // read one byte too many, back up
2353 fseek($this->getid3->fp, -1, SEEK_CUR);
2354 break;
2355 }
2356 $buffer .= $thischar;
2357 if ($thischar == "\n") {
2358 break;
2359 }
2360 if (feof($this->getid3->fp)) {
2361 break;
2362 }
2363 $prevchar = $thischar;
2364 }
2365
2366 }
2367 return $buffer;
2368 }
2369
2370 /**
2371 * @phpstan-impure
2372 *
2373 * @return bool
2374 */
2375 protected function feof() {
2376 if ($this->data_string_flag) {
2377 return $this->data_string_position >= $this->data_string_length;
2378 }
2379 return feof($this->getid3->fp);
2380 }
2381
2382 /**
2383 * @param string $module
2384 *
2385 * @return bool
2386 */
2387 final protected function isDependencyFor($module) {
2388 return $this->dependency_to == $module;
2389 }
2390
2391 /**
2392 * @param string $text
2393 *
2394 * @return bool
2395 */
2396 protected function error($text) {
2397 $this->getid3->info['error'][] = $text;
2398
2399 return false;
2400 }
2401
2402 /**
2403 * @param string $text
2404 *
2405 * @return bool
2406 */
2407 protected function warning($text) {
2408 return $this->getid3->warning($text);
2409 }
2410
2411 /**
2412 * @param string $text
2413 */
2414 protected function notice($text) {
2415 // does nothing for now
2416 }
2417
2418 /**
2419 * @param string $name
2420 * @param int $offset
2421 * @param int $length
2422 * @param string $image_mime
2423 *
2424 * @return string|null
2425 *
2426 * @throws Exception
2427 * @throws getid3_exception
2428 */
2429 public function saveAttachment($name, $offset, $length, $image_mime=null) {
2430 $fp_dest = null;
2431 $dest = null;
2432 try {
2433
2434 // do not extract at all
2435 if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
2436
2437 $attachment = null; // do not set any
2438
2439 // extract to return array
2440 } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
2441
2442 $this->fseek($offset);
2443 $attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory
2444 if ($attachment === false || strlen($attachment) != $length) {
2445 throw new Exception('failed to read attachment data');
2446 }
2447
2448 // assume directory path is given
2449 } else {
2450
2451 // set up destination path
2452 $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
2453 if (!is_dir($dir) || !getID3::is_writable($dir)) { // check supplied directory
2454 throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
2455 }
2456 $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
2457
2458 // create dest file
2459 if (($fp_dest = fopen($dest, 'wb')) == false) {
2460 throw new Exception('failed to create file '.$dest);
2461 }
2462
2463 // copy data
2464 $this->fseek($offset);
2465 $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
2466 $bytesleft = $length;
2467 while ($bytesleft > 0) {
2468 if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
2469 throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
2470 }
2471 $bytesleft -= $byteswritten;
2472 }
2473
2474 fclose($fp_dest);
2475 $attachment = $dest;
2476
2477 }
2478
2479 } catch (Exception $e) {
2480
2481 // close and remove dest file if created
2482 if (isset($fp_dest) && is_resource($fp_dest)) {
2483 fclose($fp_dest);
2484 }
2485
2486 if (isset($dest) && file_exists($dest)) {
2487 unlink($dest);
2488 }
2489
2490 // do not set any is case of error
2491 $attachment = null;
2492 $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
2493
2494 }
2495
2496 // seek to the end of attachment
2497 $this->fseek($offset + $length);
2498
2499 return $attachment;
2500 }
2501
2502}
2503
2504
2505class getid3_exception extends Exception
2506{
2507 public $message;
2508}
2509
Ui Ux Design – Teachers Night Out

Get in Touch

© 2024 Teachers Night Out. All Rights Reserved.