rtMedia for WordPress, BuddyPress and bbPress - Version 3.8.20

Version Description

  • Fix getid3 related issue
  • Other bug fixes
Download this release

Release Info

Developer rtcamp
Plugin Icon 128x128 rtMedia for WordPress, BuddyPress and bbPress
Version 3.8.20
Comparing to
See all releases

Code changes from version 3.8.19 to 3.8.20

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