Mercurial > judyates
comparison e2gallerypro/e2upload/Backend/Assets/getid3/getid3.php @ 3:3f6b44aa6b35 judyates
[svn r4] added ability to buy stuff, from a Prints page, but it doesn't work well with the css, and it also has not been fitted into the perl make system.
author | rlm |
---|---|
date | Mon, 22 Feb 2010 08:02:39 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:670229c4eb4b | 3:3f6b44aa6b35 |
---|---|
1 <?php | |
2 // +----------------------------------------------------------------------+ | |
3 // | PHP version 5 | | |
4 // +----------------------------------------------------------------------+ | |
5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | | |
6 // +----------------------------------------------------------------------+ | |
7 // | This source file is subject to version 2 of the GPL license, | | |
8 // | that is bundled with this package in the file license.txt and is | | |
9 // | available through the world-wide-web at the following url: | | |
10 // | http://www.gnu.org/copyleft/gpl.html | | |
11 // +----------------------------------------------------------------------+ | |
12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | | |
13 // +----------------------------------------------------------------------+ | |
14 // | Authors: James Heinrich <infogetid3*org> | | |
15 // | Allan Hansen <ahartemis*dk> | | |
16 // +----------------------------------------------------------------------+ | |
17 // | getid3.php | | |
18 // | Main getID3() file. | | |
19 // | dependencies: modules. | | |
20 // +----------------------------------------------------------------------+ | |
21 // | |
22 // $Id: getid3.php,v 1.26 2006/12/25 23:44:23 ah Exp $ | |
23 | |
24 | |
25 class getid3 | |
26 { | |
27 //// Settings Section - do NOT modify this file - change setting after newing getid3! | |
28 | |
29 // Encoding | |
30 public $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv() - see http://www.gnu.org/software/libiconv/). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE. | |
31 public $encoding_id3v1 = 'ISO-8859-1'; // Override SPECIFICATION encoding for broken ID3v1 tags caused by bad tag programs. Examples: 'EUC-CN' for "Chinese MP3s" and 'CP1251' for "Cyrillic". | |
32 public $encoding_id3v2 = 'ISO-8859-1'; // Override ISO-8859-1 encoding for broken ID3v2 tags caused by BRAINDEAD tag programs that writes system codepage as 'ISO-8859-1' instead of UTF-8. | |
33 | |
34 // Tags - disable for speed | |
35 public $option_tag_id3v1 = true; // Read and process ID3v1 tags. | |
36 public $option_tag_id3v2 = true; // Read and process ID3v2 tags. | |
37 public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags. | |
38 public $option_tag_apetag = true; // Read and process APE tags. | |
39 | |
40 // Misc calucations - disable for speed | |
41 public $option_analyze = true; // Analyze file - disable if you only need to detect file format. | |
42 public $option_accurate_results = true; // Disable to greatly speed up parsing of some file formats at the cost of accuracy. | |
43 public $option_tags_process = true; // Copy tags to root key 'tags' and 'comments' and encode to $this->encoding. | |
44 public $option_tags_images = false; // Scan tags for binary image data - ID3v2 and vorbiscomments only. | |
45 public $option_extra_info = true; // Calculate/return additional info such as bitrate, channelmode etc. | |
46 public $option_max_2gb_check = false; // Check whether file is larger than 2 Gb and thus not supported by PHP. | |
47 | |
48 // Misc data hashes - slow - require hash module | |
49 public $option_md5_data = false; // Get MD5 sum of data part - slow. | |
50 public $option_md5_data_source = false; // Use MD5 of source file if available - only FLAC, MAC, OptimFROG and Wavpack4. | |
51 public $option_sha1_data = false; // Get SHA1 sum of data part - slow. | |
52 | |
53 // Public variables | |
54 public $filename; // Filename of file being analysed. | |
55 public $fp; // Filepointer to file being analysed. | |
56 public $info; // Result array. | |
57 | |
58 // Protected variables | |
59 protected $include_path; // getid3 include path. | |
60 protected $warnings = array (); | |
61 protected $iconv_present; | |
62 | |
63 // Class constants | |
64 const VERSION = '2.0.0b4'; | |
65 const FREAD_BUFFER_SIZE = 16384; // Read buffer size in bytes. | |
66 const ICONV_TEST_STRING = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ '; | |
67 | |
68 | |
69 | |
70 // Constructor - check PHP enviroment and load library. | |
71 public function __construct() { | |
72 | |
73 // Static varibles - no need to recalc every time we new getid3. | |
74 static $include_path; | |
75 static $iconv_present; | |
76 | |
77 | |
78 static $initialized; | |
79 if ($initialized) { | |
80 | |
81 // Import static variables | |
82 $this->include_path = $include_path; | |
83 $this->iconv_present = $iconv_present; | |
84 | |
85 // Run init checks only on first instance. | |
86 return; | |
87 } | |
88 | |
89 // Get include_path | |
90 $this->include_path = $include_path = dirname(__FILE__) . '/'; | |
91 | |
92 // Check for presence of iconv() and make sure it works (simpel test only). | |
93 if (function_exists('iconv') && @iconv('UTF-16LE', 'ISO-8859-1', @iconv('ISO-8859-1', 'UTF-16LE', getid3::ICONV_TEST_STRING)) == getid3::ICONV_TEST_STRING) { | |
94 $this->iconv_present = $iconv_present = true; | |
95 } | |
96 | |
97 // iconv() not present - load replacement module. | |
98 else { | |
99 $this->include_module('lib.iconv_replacement'); | |
100 $this->iconv_present = $iconv_present = false; | |
101 } | |
102 | |
103 | |
104 // Require magic_quotes_runtime off | |
105 if (get_magic_quotes_runtime()) { | |
106 throw new getid3_exception('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'); | |
107 } | |
108 | |
109 | |
110 // Check memory limit. | |
111 $memory_limit = ini_get('memory_limit'); | |
112 if (eregi('([0-9]+)M', $memory_limit, $matches)) { | |
113 // could be stored as "16M" rather than 16777216 for example | |
114 $memory_limit = $matches[1] * 1048576; | |
115 } | |
116 if ($memory_limit <= 0) { | |
117 // Should not happen. | |
118 } elseif ($memory_limit <= 4194304) { | |
119 $this->warning('[SERIOUS] PHP has less than 4 Mb available memory and will very likely run out. Increase memory_limit in php.ini.'); | |
120 } elseif ($memory_limit <= 12582912) { | |
121 $this->warning('PHP has less than 12 Mb available memory and might run out if all modules are loaded. Increase memory_limit in php.ini if needed.'); | |
122 } | |
123 | |
124 | |
125 // Check safe_mode off | |
126 if ((bool)ini_get('safe_mode')) { | |
127 $this->warning('Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbis/flac tag writing disabled.'); | |
128 } | |
129 | |
130 $initialized = true; | |
131 } | |
132 | |
133 | |
134 | |
135 // Analyze file by name | |
136 public function Analyze($filename) { | |
137 | |
138 // Init and save values | |
139 $this->filename = $filename; | |
140 $this->warnings = array (); | |
141 | |
142 // Init result array and set parameters | |
143 $this->info = array (); | |
144 $this->info['GETID3_VERSION'] = getid3::VERSION; | |
145 | |
146 // Remote files not supported | |
147 if (preg_match('/^(ht|f)tp:\/\//', $filename)) { | |
148 throw new getid3_exception('Remote files are not supported - please copy the file locally first.'); | |
149 } | |
150 | |
151 // Open local file | |
152 if (!$this->fp = @fopen($filename, 'rb')) { | |
153 throw new getid3_exception('Could not open file "'.$filename.'"'); | |
154 } | |
155 | |
156 // Set filesize related parameters | |
157 $this->info['filesize'] = filesize($filename); | |
158 $this->info['avdataoffset'] = 0; | |
159 $this->info['avdataend'] = $this->info['filesize']; | |
160 | |
161 // Option_max_2gb_check | |
162 if ($this->option_max_2gb_check) { | |
163 // PHP doesn't support integers larger than 31-bit (~2GB) | |
164 // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize | |
165 // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer | |
166 fseek($this->fp, 0, SEEK_END); | |
167 if ((($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) || | |
168 ($this->info['filesize'] < 0) || | |
169 (ftell($this->fp) < 0)) { | |
170 unset($this->info['filesize']); | |
171 fclose($this->fp); | |
172 throw new getid3_exception('File is most likely larger than 2GB and is not supported by PHP.'); | |
173 } | |
174 } | |
175 | |
176 | |
177 // ID3v2 detection (NOT parsing) done to make fileformat easier. | |
178 if (!$this->option_tag_id3v2) { | |
179 | |
180 fseek($this->fp, 0, SEEK_SET); | |
181 $header = fread($this->fp, 10); | |
182 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { | |
183 $this->info['id3v2']['header'] = true; | |
184 $this->info['id3v2']['majorversion'] = ord($header{3}); | |
185 $this->info['id3v2']['minorversion'] = ord($header{4}); | |
186 $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 | |
187 } | |
188 } | |
189 | |
190 | |
191 // Handle tags | |
192 foreach (array ("id3v2", "id3v1", "apetag", "lyrics3") as $tag_name) { | |
193 | |
194 $option_tag = 'option_tag_' . $tag_name; | |
195 if ($this->$option_tag) { | |
196 $this->include_module('tag.'.$tag_name); | |
197 try { | |
198 $tag_class = 'getid3_' . $tag_name; | |
199 $tag = new $tag_class($this); | |
200 $tag->Analyze(); | |
201 } | |
202 catch (getid3_exception $e) { | |
203 throw $e; | |
204 } | |
205 } | |
206 } | |
207 | |
208 | |
209 | |
210 //// Determine file format by magic bytes in file header. | |
211 | |
212 // Read 32 kb file data | |
213 fseek($this->fp, $this->info['avdataoffset'], SEEK_SET); | |
214 $filedata = fread($this->fp, 32774); | |
215 | |
216 // Get huge FileFormatArray | |
217 $file_format_array = getid3::GetFileFormatArray(); | |
218 | |
219 // Identify file format - loop through $format_info and detect with reg expr | |
220 foreach ($file_format_array as $name => $info) { | |
221 | |
222 if (preg_match('/'.$info['pattern'].'/s', $filedata)) { // The /s switch on preg_match() forces preg_match() NOT to treat newline (0x0A) characters as special chars but do a binary match | |
223 | |
224 // Format detected but not supported | |
225 if (!@$info['module'] || !@$info['group']) { | |
226 fclose($this->fp); | |
227 $this->info['fileformat'] = $name; | |
228 $this->info['mime_type'] = $info['mime_type']; | |
229 $this->warning('Format only detected. Parsing not available yet.'); | |
230 $this->info['warning'] = $this->warnings; | |
231 return $this->info; | |
232 } | |
233 | |
234 $determined_format = $info; // copy $info deleted by foreach() | |
235 continue; | |
236 } | |
237 } | |
238 | |
239 // Unable to determine file format | |
240 if (!@$determined_format) { | |
241 | |
242 // Too many mp3 encoders on the market put gabage in front of mpeg files | |
243 // use assume format on these if format detection failed | |
244 if (preg_match('/\.mp[123a]$/i', $filename)) { | |
245 $determined_format = $file_format_array['mp3']; | |
246 } | |
247 | |
248 else { | |
249 fclose($this->fp); | |
250 throw new getid3_exception('Unable to determine file format'); | |
251 } | |
252 } | |
253 | |
254 // Free memory | |
255 unset($file_format_array); | |
256 | |
257 // Check for illegal ID3 tags | |
258 if (@$determined_format['fail_id3'] && (@$this->info['id3v1'] || @$this->info['id3v2'])) { | |
259 if ($determined_format['fail_id3'] === 'ERROR') { | |
260 fclose($this->fp); | |
261 throw new getid3_exception('ID3 tags not allowed on this file type.'); | |
262 } | |
263 elseif ($determined_format['fail_id3'] === 'WARNING') { | |
264 @$this->info['id3v1'] and $this->warning('ID3v1 tags not allowed on this file type.'); | |
265 @$this->info['id3v2'] and $this->warning('ID3v2 tags not allowed on this file type.'); | |
266 } | |
267 } | |
268 | |
269 // Check for illegal APE tags | |
270 if (@$determined_format['fail_ape'] && @$this->info['tags']['ape']) { | |
271 if ($determined_format['fail_ape'] === 'ERROR') { | |
272 fclose($this->fp); | |
273 throw new getid3_exception('APE tags not allowed on this file type.'); | |
274 } elseif ($determined_format['fail_ape'] === 'WARNING') { | |
275 $this->warning('APE tags not allowed on this file type.'); | |
276 } | |
277 } | |
278 | |
279 | |
280 // Set mime type | |
281 $this->info['mime_type'] = $determined_format['mime_type']; | |
282 | |
283 // Calc module file name | |
284 $determined_format['include'] = 'module.'.$determined_format['group'].'.'.$determined_format['module'].'.php'; | |
285 | |
286 // Supported format signature pattern detected, but module deleted. | |
287 if (!file_exists($this->include_path.$determined_format['include'])) { | |
288 fclose($this->fp); | |
289 throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', was removed.'); | |
290 } | |
291 | |
292 // Include module | |
293 $this->include_module($determined_format['group'].'.'.$determined_format['module']); | |
294 | |
295 // Instantiate module class and analyze | |
296 $class_name = 'getid3_'.$determined_format['module']; | |
297 if (!class_exists($class_name)) { | |
298 throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', is corrupt.'); | |
299 } | |
300 $class = new $class_name($this); | |
301 | |
302 try { | |
303 $this->option_analyze and $class->Analyze(); | |
304 } | |
305 catch (getid3_exception $e) { | |
306 throw $e; | |
307 } | |
308 catch (Exception $e) { | |
309 throw new getid3_exception('Corrupt file.'); | |
310 } | |
311 | |
312 // Close file | |
313 fclose($this->fp); | |
314 | |
315 // Optional - Process all tags - copy to 'tags' and convert charsets | |
316 if ($this->option_tags_process) { | |
317 $this->HandleAllTags(); | |
318 } | |
319 | |
320 | |
321 //// Optional - perform more calculations | |
322 if ($this->option_extra_info) { | |
323 | |
324 // Set channelmode on audio | |
325 if (@$this->info['audio']['channels'] == '1') { | |
326 $this->info['audio']['channelmode'] = 'mono'; | |
327 } elseif (@$this->info['audio']['channels'] == '2') { | |
328 $this->info['audio']['channelmode'] = 'stereo'; | |
329 } | |
330 | |
331 // Calculate combined bitrate - audio + video | |
332 $combined_bitrate = 0; | |
333 $combined_bitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); | |
334 $combined_bitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); | |
335 if (($combined_bitrate > 0) && empty($this->info['bitrate'])) { | |
336 $this->info['bitrate'] = $combined_bitrate; | |
337 } | |
338 if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) { | |
339 $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; | |
340 } | |
341 | |
342 // Set playtime string | |
343 if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { | |
344 $this->info['playtime_string'] = floor(round($this->info['playtime_seconds']) / 60) . ':' . str_pad(floor(round($this->info['playtime_seconds']) % 60), 2, 0, STR_PAD_LEFT);; | |
345 } | |
346 | |
347 | |
348 // CalculateCompressionRatioVideo() { | |
349 if (@$this->info['video'] && @$this->info['video']['resolution_x'] && @$this->info['video']['resolution_y'] && @$this->info['video']['bits_per_sample']) { | |
350 | |
351 // From static image formats | |
352 if (in_array($this->info['video']['dataformat'], array ('bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff'))) { | |
353 $frame_rate = 1; | |
354 $bitrate_compressed = $this->info['filesize'] * 8; | |
355 } | |
356 | |
357 // From video formats | |
358 else { | |
359 $frame_rate = @$this->info['video']['frame_rate']; | |
360 $bitrate_compressed = @$this->info['video']['bitrate']; | |
361 } | |
362 | |
363 if ($frame_rate && $bitrate_compressed) { | |
364 $this->info['video']['compression_ratio'] = $bitrate_compressed / ($this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $frame_rate); | |
365 } | |
366 } | |
367 | |
368 | |
369 // CalculateCompressionRatioAudio() { | |
370 if (@$this->info['audio']['bitrate'] && @$this->info['audio']['channels'] && @$this->info['audio']['sample_rate']) { | |
371 $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (@$this->info['audio']['bits_per_sample'] ? $this->info['audio']['bits_per_sample'] : 16)); | |
372 } | |
373 | |
374 if (@$this->info['audio']['streams']) { | |
375 foreach ($this->info['audio']['streams'] as $stream_number => $stream_data) { | |
376 if (@$stream_data['bitrate'] && @$stream_data['channels'] && @$stream_data['sample_rate']) { | |
377 $this->info['audio']['streams'][$stream_number]['compression_ratio'] = $stream_data['bitrate'] / ($stream_data['channels'] * $stream_data['sample_rate'] * (@$stream_data['bits_per_sample'] ? $stream_data['bits_per_sample'] : 16)); | |
378 } | |
379 } | |
380 } | |
381 | |
382 | |
383 // CalculateReplayGain() { | |
384 if (@$this->info['replay_gain']) { | |
385 if (!@$this->info['replay_gain']['reference_volume']) { | |
386 $this->info['replay_gain']['reference_volume'] = 89; | |
387 } | |
388 if (isset($this->info['replay_gain']['track']['adjustment'])) { | |
389 $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; | |
390 } | |
391 if (isset($this->info['replay_gain']['album']['adjustment'])) { | |
392 $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; | |
393 } | |
394 | |
395 if (isset($this->info['replay_gain']['track']['peak'])) { | |
396 $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['track']['peak']); | |
397 } | |
398 if (isset($this->info['replay_gain']['album']['peak'])) { | |
399 $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['album']['peak']); | |
400 } | |
401 } | |
402 | |
403 | |
404 // ProcessAudioStreams() { | |
405 if (@!$this->info['audio']['streams'] && (@$this->info['audio']['bitrate'] || @$this->info['audio']['channels'] || @$this->info['audio']['sample_rate'])) { | |
406 foreach ($this->info['audio'] as $key => $value) { | |
407 if ($key != 'streams') { | |
408 $this->info['audio']['streams'][0][$key] = $value; | |
409 } | |
410 } | |
411 } | |
412 } | |
413 | |
414 | |
415 // Get the md5/sha1sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags. | |
416 if ($this->option_md5_data || $this->option_sha1_data) { | |
417 | |
418 // Load data-hash library if needed | |
419 $this->include_module('lib.data_hash'); | |
420 | |
421 if ($this->option_sha1_data) { | |
422 new getid3_lib_data_hash($this, 'sha1'); | |
423 } | |
424 | |
425 if ($this->option_md5_data) { | |
426 | |
427 // no md5_data_source or option disabled -- md5_data_source supported by FLAC, MAC, OptimFROG, Wavpack4 | |
428 if (!$this->option_md5_data_source || !@$this->info['md5_data_source']) { | |
429 new getid3_lib_data_hash($this, 'md5'); | |
430 } | |
431 | |
432 // copy md5_data_source to md5_data if option set to true | |
433 elseif ($this->option_md5_data_source && @$this->info['md5_data_source']) { | |
434 $this->info['md5_data'] = $this->info['md5_data_source']; | |
435 } | |
436 } | |
437 } | |
438 | |
439 // Set warnings | |
440 if ($this->warnings) { | |
441 $this->info['warning'] = $this->warnings; | |
442 } | |
443 | |
444 // Return result | |
445 return $this->info; | |
446 } | |
447 | |
448 | |
449 | |
450 // Return array of warnings | |
451 public function warnings() { | |
452 | |
453 return $this->warnings; | |
454 } | |
455 | |
456 | |
457 | |
458 // Add warning(s) to $this->warnings[] | |
459 public function warning($message) { | |
460 | |
461 if (is_array($message)) { | |
462 $this->warnings = array_merge($this->warnings, $message); | |
463 } | |
464 else { | |
465 $this->warnings[] = $message; | |
466 } | |
467 } | |
468 | |
469 | |
470 | |
471 // Clear all warnings when cloning | |
472 public function __clone() { | |
473 | |
474 $this->warnings = array (); | |
475 | |
476 // Copy info array, otherwise it will be a reference. | |
477 $temp = $this->info; | |
478 unset($this->info); | |
479 $this->info = $temp; | |
480 } | |
481 | |
482 | |
483 | |
484 // Convert string between charsets -- iconv() wrapper | |
485 public function iconv($in_charset, $out_charset, $string, $drop01 = false) { | |
486 | |
487 if ($drop01 && ($string === "\x00" || $string === "\x01")) { | |
488 return ''; | |
489 } | |
490 | |
491 | |
492 if (!$this->iconv_present) { | |
493 return getid3_iconv_replacement::iconv($in_charset, $out_charset, $string); | |
494 } | |
495 | |
496 | |
497 // iconv() present | |
498 if ($result = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { | |
499 | |
500 if ($out_charset == 'ISO-8859-1') { | |
501 return rtrim($result, "\x00"); | |
502 } | |
503 return $result; | |
504 } | |
505 | |
506 $this->warning('iconv() was unable to convert the string: "' . $string . '" from ' . $in_charset . ' to ' . $out_charset); | |
507 return $string; | |
508 } | |
509 | |
510 | |
511 | |
512 public function include_module($name) { | |
513 | |
514 if (!file_exists($this->include_path.'module.'.$name.'.php')) { | |
515 throw new getid3_exception('Required module.'.$name.'.php is missing.'); | |
516 } | |
517 | |
518 include_once($this->include_path.'module.'.$name.'.php'); | |
519 } | |
520 | |
521 | |
522 | |
523 public function include_module_optional($name) { | |
524 | |
525 if (!file_exists($this->include_path.'module.'.$name.'.php')) { | |
526 return; | |
527 } | |
528 | |
529 include_once($this->include_path.'module.'.$name.'.php'); | |
530 return true; | |
531 } | |
532 | |
533 | |
534 // Return array containing information about all supported formats | |
535 public static function GetFileFormatArray() { | |
536 | |
537 static $format_info = array ( | |
538 | |
539 // Audio formats | |
540 | |
541 // AC-3 - audio - Dolby AC-3 / Dolby Digital | |
542 'ac3' => array ( | |
543 'pattern' => '^\x0B\x77', | |
544 'group' => 'audio', | |
545 'module' => 'ac3', | |
546 'mime_type' => 'audio/ac3', | |
547 ), | |
548 | |
549 // AAC - audio - Advanced Audio Coding (AAC) - ADIF format | |
550 'adif' => array ( | |
551 'pattern' => '^ADIF', | |
552 'group' => 'audio', | |
553 'module' => 'aac_adif', | |
554 'mime_type' => 'application/octet-stream', | |
555 'fail_ape' => 'WARNING', | |
556 ), | |
557 | |
558 | |
559 // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) | |
560 'adts' => array ( | |
561 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', | |
562 'group' => 'audio', | |
563 'module' => 'aac_adts', | |
564 'mime_type' => 'application/octet-stream', | |
565 'fail_ape' => 'WARNING', | |
566 ), | |
567 | |
568 | |
569 // AU - audio - NeXT/Sun AUdio (AU) | |
570 'au' => array ( | |
571 'pattern' => '^\.snd', | |
572 'group' => 'audio', | |
573 'module' => 'au', | |
574 'mime_type' => 'audio/basic', | |
575 ), | |
576 | |
577 // AVR - audio - Audio Visual Research | |
578 'avr' => array ( | |
579 'pattern' => '^2BIT', | |
580 'group' => 'audio', | |
581 'module' => 'avr', | |
582 'mime_type' => 'application/octet-stream', | |
583 ), | |
584 | |
585 // BONK - audio - Bonk v0.9+ | |
586 'bonk' => array ( | |
587 'pattern' => '^\x00(BONK|INFO|META| ID3)', | |
588 'group' => 'audio', | |
589 'module' => 'bonk', | |
590 'mime_type' => 'audio/xmms-bonk', | |
591 ), | |
592 | |
593 // DTS - audio - Dolby Theatre System | |
594 'dts' => array( | |
595 'pattern' => '^\x7F\xFE\x80\x01', | |
596 'group' => 'audio', | |
597 'module' => 'dts', | |
598 'mime_type' => 'audio/dts', | |
599 ), | |
600 | |
601 // FLAC - audio - Free Lossless Audio Codec | |
602 'flac' => array ( | |
603 'pattern' => '^fLaC', | |
604 'group' => 'audio', | |
605 'module' => 'xiph', | |
606 'mime_type' => 'audio/x-flac', | |
607 ), | |
608 | |
609 // LA - audio - Lossless Audio (LA) | |
610 'la' => array ( | |
611 'pattern' => '^LA0[2-4]', | |
612 'group' => 'audio', | |
613 'module' => 'la', | |
614 'mime_type' => 'application/octet-stream', | |
615 ), | |
616 | |
617 // LPAC - audio - Lossless Predictive Audio Compression (LPAC) | |
618 'lpac' => array ( | |
619 'pattern' => '^LPAC', | |
620 'group' => 'audio', | |
621 'module' => 'lpac', | |
622 'mime_type' => 'application/octet-stream', | |
623 ), | |
624 | |
625 // MIDI - audio - MIDI (Musical Instrument Digital Interface) | |
626 'midi' => array ( | |
627 'pattern' => '^MThd', | |
628 'group' => 'audio', | |
629 'module' => 'midi', | |
630 'mime_type' => 'audio/midi', | |
631 ), | |
632 | |
633 // MAC - audio - Monkey's Audio Compressor | |
634 'mac' => array ( | |
635 'pattern' => '^MAC ', | |
636 'group' => 'audio', | |
637 'module' => 'monkey', | |
638 'mime_type' => 'application/octet-stream', | |
639 ), | |
640 | |
641 // MOD - audio - MODule (assorted sub-formats) | |
642 'mod' => array ( | |
643 'pattern' => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)', | |
644 'mime_type' => 'audio/mod', | |
645 ), | |
646 | |
647 // MOD - audio - MODule (Impulse Tracker) | |
648 'it' => array ( | |
649 'pattern' => '^IMPM', | |
650 'mime_type' => 'audio/it', | |
651 ), | |
652 | |
653 // MOD - audio - MODule (eXtended Module, various sub-formats) | |
654 'xm' => array ( | |
655 'pattern' => '^Extended Module', | |
656 'mime_type' => 'audio/xm', | |
657 ), | |
658 | |
659 // MOD - audio - MODule (ScreamTracker) | |
660 's3m' => array ( | |
661 'pattern' => '^.{44}SCRM', | |
662 'mime_type' => 'audio/s3m', | |
663 ), | |
664 | |
665 // MPC - audio - Musepack / MPEGplus SV7+ | |
666 'mpc' => array ( | |
667 'pattern' => '^(MP\+)', | |
668 'group' => 'audio', | |
669 'module' => 'mpc', | |
670 'mime_type' => 'audio/x-musepack', | |
671 ), | |
672 | |
673 // MPC - audio - Musepack / MPEGplus SV4-6 | |
674 'mpc_old' => array ( | |
675 'pattern' => '^([\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])', | |
676 'group' => 'audio', | |
677 'module' => 'mpc_old', | |
678 'mime_type' => 'application/octet-stream', | |
679 ), | |
680 | |
681 | |
682 // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) | |
683 'mp3' => array ( | |
684 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', | |
685 'group' => 'audio', | |
686 'module' => 'mp3', | |
687 'mime_type' => 'audio/mpeg', | |
688 ), | |
689 | |
690 // OFR - audio - OptimFROG | |
691 'ofr' => array ( | |
692 'pattern' => '^(\*RIFF|OFR)', | |
693 'group' => 'audio', | |
694 'module' => 'optimfrog', | |
695 'mime_type' => 'application/octet-stream', | |
696 ), | |
697 | |
698 // RKAU - audio - RKive AUdio compressor | |
699 'rkau' => array ( | |
700 'pattern' => '^RKA', | |
701 'group' => 'audio', | |
702 'module' => 'rkau', | |
703 'mime_type' => 'application/octet-stream', | |
704 ), | |
705 | |
706 // SHN - audio - Shorten | |
707 'shn' => array ( | |
708 'pattern' => '^ajkg', | |
709 'group' => 'audio', | |
710 'module' => 'shorten', | |
711 'mime_type' => 'audio/xmms-shn', | |
712 'fail_id3' => 'ERROR', | |
713 'fail_ape' => 'ERROR', | |
714 ), | |
715 | |
716 // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) | |
717 'tta' => array ( | |
718 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' | |
719 'group' => 'audio', | |
720 'module' => 'tta', | |
721 'mime_type' => 'application/octet-stream', | |
722 ), | |
723 | |
724 // VOC - audio - Creative Voice (VOC) | |
725 'voc' => array ( | |
726 'pattern' => '^Creative Voice File', | |
727 'group' => 'audio', | |
728 'module' => 'voc', | |
729 'mime_type' => 'audio/voc', | |
730 ), | |
731 | |
732 // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) | |
733 'vqf' => array ( | |
734 'pattern' => '^TWIN', | |
735 'group' => 'audio', | |
736 'module' => 'vqf', | |
737 'mime_type' => 'application/octet-stream', | |
738 ), | |
739 | |
740 // WV - audio - WavPack (v4.0+) | |
741 'vw' => array( | |
742 'pattern' => '^wvpk', | |
743 'group' => 'audio', | |
744 'module' => 'wavpack', | |
745 'mime_type' => 'application/octet-stream', | |
746 ), | |
747 | |
748 | |
749 // Audio-Video formats | |
750 | |
751 // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio | |
752 'asf' => array ( | |
753 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', | |
754 'group' => 'audio-video', | |
755 'module' => 'asf', | |
756 'mime_type' => 'video/x-ms-asf', | |
757 ), | |
758 | |
759 // BINK - audio/video - Bink / Smacker | |
760 'bink' => array( | |
761 'pattern' => '^(BIK|SMK)', | |
762 'mime_type' => 'application/octet-stream', | |
763 ), | |
764 | |
765 // FLV - audio/video - FLash Video | |
766 'flv' => array( | |
767 'pattern' => '^FLV\x01', | |
768 'group' => 'audio-video', | |
769 'module' => 'flv', | |
770 'mime_type' => 'video/x-flv', | |
771 ), | |
772 | |
773 // MKAV - audio/video - Mastroka | |
774 'matroska' => array ( | |
775 'pattern' => '^\x1A\x45\xDF\xA3', | |
776 'mime_type' => 'application/octet-stream', | |
777 ), | |
778 | |
779 // MPEG - audio/video - MPEG (Moving Pictures Experts Group) | |
780 'mpeg' => array ( | |
781 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', | |
782 'group' => 'audio-video', | |
783 'module' => 'mpeg', | |
784 'mime_type' => 'video/mpeg', | |
785 ), | |
786 | |
787 // NSV - audio/video - Nullsoft Streaming Video (NSV) | |
788 'nsv' => array ( | |
789 'pattern' => '^NSV[sf]', | |
790 'group' => 'audio-video', | |
791 'module' => 'nsv', | |
792 'mime_type' => 'application/octet-stream', | |
793 ), | |
794 | |
795 // Ogg - audio/video - Ogg (Ogg Vorbis, OggFLAC, Speex, Ogg Theora(*), Ogg Tarkin(*)) | |
796 'ogg' => array ( | |
797 'pattern' => '^OggS', | |
798 'group' => 'audio', | |
799 'module' => 'xiph', | |
800 'mime_type' => 'application/ogg', | |
801 'fail_id3' => 'WARNING', | |
802 'fail_ape' => 'WARNING', | |
803 ), | |
804 | |
805 // QT - audio/video - Quicktime | |
806 'quicktime' => array ( | |
807 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', | |
808 'group' => 'audio-video', | |
809 'module' => 'quicktime', | |
810 'mime_type' => 'video/quicktime', | |
811 ), | |
812 | |
813 // 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) | |
814 'riff' => array ( | |
815 'pattern' => '^(RIFF|SDSS|FORM)', | |
816 'group' => 'audio-video', | |
817 'module' => 'riff', | |
818 'mime_type' => 'audio/x-wave', | |
819 'fail_ape' => 'WARNING', | |
820 ), | |
821 | |
822 // Real - audio/video - RealAudio, RealVideo | |
823 'real' => array ( | |
824 'pattern' => '^(\.RMF|.ra)', | |
825 'group' => 'audio-video', | |
826 'module' => 'real', | |
827 'mime_type' => 'audio/x-realaudio', | |
828 ), | |
829 | |
830 // SWF - audio/video - ShockWave Flash | |
831 'swf' => array ( | |
832 'pattern' => '^(F|C)WS', | |
833 'group' => 'audio-video', | |
834 'module' => 'swf', | |
835 'mime_type' => 'application/x-shockwave-flash', | |
836 ), | |
837 | |
838 | |
839 // Still-Image formats | |
840 | |
841 // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) | |
842 'bmp' => array ( | |
843 'pattern' => '^BM', | |
844 'group' => 'graphic', | |
845 'module' => 'bmp', | |
846 'mime_type' => 'image/bmp', | |
847 'fail_id3' => 'ERROR', | |
848 'fail_ape' => 'ERROR', | |
849 ), | |
850 | |
851 // GIF - still image - Graphics Interchange Format | |
852 'gif' => array ( | |
853 'pattern' => '^GIF', | |
854 'group' => 'graphic', | |
855 'module' => 'gif', | |
856 'mime_type' => 'image/gif', | |
857 'fail_id3' => 'ERROR', | |
858 'fail_ape' => 'ERROR', | |
859 ), | |
860 | |
861 // JPEG - still image - Joint Photographic Experts Group (JPEG) | |
862 'jpeg' => array ( | |
863 'pattern' => '^\xFF\xD8\xFF', | |
864 'group' => 'graphic', | |
865 'module' => 'jpeg', | |
866 'mime_type' => 'image/jpeg', | |
867 'fail_id3' => 'ERROR', | |
868 'fail_ape' => 'ERROR', | |
869 ), | |
870 | |
871 // PCD - still image - Kodak Photo CD | |
872 'pcd' => array ( | |
873 'pattern' => '^.{2048}PCD_IPI\x00', | |
874 'group' => 'graphic', | |
875 'module' => 'pcd', | |
876 'mime_type' => 'image/x-photo-cd', | |
877 'fail_id3' => 'ERROR', | |
878 'fail_ape' => 'ERROR', | |
879 ), | |
880 | |
881 | |
882 // PNG - still image - Portable Network Graphics (PNG) | |
883 'png' => array ( | |
884 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', | |
885 'group' => 'graphic', | |
886 'module' => 'png', | |
887 'mime_type' => 'image/png', | |
888 'fail_id3' => 'ERROR', | |
889 'fail_ape' => 'ERROR', | |
890 ), | |
891 | |
892 | |
893 // SVG - still image - Scalable Vector Graphics (SVG) | |
894 'svg' => array( | |
895 'pattern' => '<!DOCTYPE svg PUBLIC ', | |
896 'mime_type' => 'image/svg+xml', | |
897 'fail_id3' => 'ERROR', | |
898 'fail_ape' => 'ERROR', | |
899 ), | |
900 | |
901 | |
902 // TIFF - still image - Tagged Information File Format (TIFF) | |
903 'tiff' => array ( | |
904 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', | |
905 'group' => 'graphic', | |
906 'module' => 'tiff', | |
907 'mime_type' => 'image/tiff', | |
908 'fail_id3' => 'ERROR', | |
909 'fail_ape' => 'ERROR', | |
910 ), | |
911 | |
912 | |
913 // Data formats | |
914 | |
915 'exe' => array( | |
916 'pattern' => '^MZ', | |
917 'mime_type' => 'application/octet-stream', | |
918 'fail_id3' => 'ERROR', | |
919 'fail_ape' => 'ERROR', | |
920 ), | |
921 | |
922 // ISO - data - International Standards Organization (ISO) CD-ROM Image | |
923 'iso' => array ( | |
924 'pattern' => '^.{32769}CD001', | |
925 'group' => 'misc', | |
926 'module' => 'iso', | |
927 'mime_type' => 'application/octet-stream', | |
928 'fail_id3' => 'ERROR', | |
929 'fail_ape' => 'ERROR', | |
930 ), | |
931 | |
932 // RAR - data - RAR compressed data | |
933 'rar' => array( | |
934 'pattern' => '^Rar\!', | |
935 'mime_type' => 'application/octet-stream', | |
936 'fail_id3' => 'ERROR', | |
937 'fail_ape' => 'ERROR', | |
938 ), | |
939 | |
940 // SZIP - audio - SZIP compressed data | |
941 'szip' => array ( | |
942 'pattern' => '^SZ\x0A\x04', | |
943 'group' => 'archive', | |
944 'module' => 'szip', | |
945 'mime_type' => 'application/octet-stream', | |
946 'fail_id3' => 'ERROR', | |
947 'fail_ape' => 'ERROR', | |
948 ), | |
949 | |
950 // TAR - data - TAR compressed data | |
951 'tar' => array( | |
952 '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}', | |
953 'group' => 'archive', | |
954 'module' => 'tar', | |
955 'mime_type' => 'application/x-tar', | |
956 'fail_id3' => 'ERROR', | |
957 'fail_ape' => 'ERROR', | |
958 ), | |
959 | |
960 // GZIP - data - GZIP compressed data | |
961 'gz' => array( | |
962 'pattern' => '^\x1F\x8B\x08', | |
963 'group' => 'archive', | |
964 'module' => 'gzip', | |
965 'mime_type' => 'application/x-gzip', | |
966 'fail_id3' => 'ERROR', | |
967 'fail_ape' => 'ERROR', | |
968 ), | |
969 | |
970 | |
971 // ZIP - data - ZIP compressed data | |
972 'zip' => array ( | |
973 'pattern' => '^PK\x03\x04', | |
974 'group' => 'archive', | |
975 'module' => 'zip', | |
976 'mime_type' => 'application/zip', | |
977 'fail_id3' => 'ERROR', | |
978 'fail_ape' => 'ERROR', | |
979 ), | |
980 | |
981 | |
982 // PAR2 - data - Parity Volume Set Specification 2.0 | |
983 'par2' => array ( | |
984 'pattern' => '^PAR2\x00PKT', | |
985 'mime_type' => 'application/octet-stream', | |
986 'fail_id3' => 'ERROR', | |
987 'fail_ape' => 'ERROR', | |
988 ), | |
989 | |
990 | |
991 // PDF - data - Portable Document Format | |
992 'pdf' => array( | |
993 'pattern' => '^\x25PDF', | |
994 'mime_type' => 'application/pdf', | |
995 'fail_id3' => 'ERROR', | |
996 'fail_ape' => 'ERROR', | |
997 ), | |
998 | |
999 // DOC - data - Microsoft Word | |
1000 'msoffice' => array( | |
1001 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document | |
1002 'mime_type' => 'application/octet-stream', | |
1003 'fail_id3' => 'ERROR', | |
1004 'fail_ape' => 'ERROR', | |
1005 ), | |
1006 ); | |
1007 | |
1008 return $format_info; | |
1009 } | |
1010 | |
1011 | |
1012 | |
1013 // Recursive over array - converts array to $encoding charset from $this->encoding | |
1014 function CharConvert(&$array, $encoding) { | |
1015 | |
1016 // Identical encoding - end here | |
1017 if ($encoding == $this->encoding) { | |
1018 return; | |
1019 } | |
1020 | |
1021 // Loop thru array | |
1022 foreach ($array as $key => $value) { | |
1023 | |
1024 // Go recursive | |
1025 if (is_array($value)) { | |
1026 $this->CharConvert($array[$key], $encoding); | |
1027 } | |
1028 | |
1029 // Convert string | |
1030 elseif (is_string($value)) { | |
1031 $array[$key] = $this->iconv($encoding, $this->encoding, $value); | |
1032 } | |
1033 } | |
1034 } | |
1035 | |
1036 | |
1037 | |
1038 // Convert and copy tags | |
1039 protected function HandleAllTags() { | |
1040 | |
1041 // Key name => array (tag name, character encoding) | |
1042 static $tags = array ( | |
1043 'asf' => array ('asf', 'UTF-16LE'), | |
1044 'midi' => array ('midi', 'ISO-8859-1'), | |
1045 'nsv' => array ('nsv', 'ISO-8859-1'), | |
1046 'ogg' => array ('vorbiscomment', 'UTF-8'), | |
1047 'png' => array ('png', 'UTF-8'), | |
1048 'tiff' => array ('tiff', 'ISO-8859-1'), | |
1049 'quicktime' => array ('quicktime', 'ISO-8859-1'), | |
1050 'real' => array ('real', 'ISO-8859-1'), | |
1051 'vqf' => array ('vqf', 'ISO-8859-1'), | |
1052 'zip' => array ('zip', 'ISO-8859-1'), | |
1053 'riff' => array ('riff', 'ISO-8859-1'), | |
1054 'lyrics3' => array ('lyrics3', 'ISO-8859-1'), | |
1055 'id3v1' => array ('id3v1', ''), // change below - cannot assign variable to static array | |
1056 'id3v2' => array ('id3v2', 'UTF-8'), // module converts all frames to UTF-8 | |
1057 'ape' => array ('ape', 'UTF-8') | |
1058 ); | |
1059 $tags['id3v1'][1] = $this->encoding_id3v1; | |
1060 | |
1061 // Loop thru tags array | |
1062 foreach ($tags as $comment_name => $tag_name_encoding_array) { | |
1063 list($tag_name, $encoding) = $tag_name_encoding_array; | |
1064 | |
1065 // Fill in default encoding type if not already present | |
1066 @$this->info[$comment_name] and $this->info[$comment_name]['encoding'] = $encoding; | |
1067 | |
1068 // Copy comments if key name set | |
1069 if (@$this->info[$comment_name]['comments']) { | |
1070 | |
1071 foreach ($this->info[$comment_name]['comments'] as $tag_key => $value_array) { | |
1072 foreach ($value_array as $key => $value) { | |
1073 if (strlen(trim($value)) > 0) { | |
1074 $this->info['tags'][$tag_name][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! | |
1075 } | |
1076 } | |
1077 | |
1078 } | |
1079 | |
1080 if (!@$this->info['tags'][$tag_name]) { | |
1081 // comments are set but contain nothing but empty strings, so skip | |
1082 continue; | |
1083 } | |
1084 | |
1085 $this->CharConvert($this->info['tags'][$tag_name], $encoding); | |
1086 } | |
1087 } | |
1088 | |
1089 | |
1090 // Merge comments from ['tags'] into common ['comments'] | |
1091 if (@$this->info['tags']) { | |
1092 | |
1093 foreach ($this->info['tags'] as $tag_type => $tag_array) { | |
1094 | |
1095 foreach ($tag_array as $tag_name => $tagdata) { | |
1096 | |
1097 foreach ($tagdata as $key => $value) { | |
1098 | |
1099 if (!empty($value)) { | |
1100 | |
1101 if (empty($this->info['comments'][$tag_name])) { | |
1102 | |
1103 // fall through and append value | |
1104 } | |
1105 elseif ($tag_type == 'id3v1') { | |
1106 | |
1107 $new_value_length = strlen(trim($value)); | |
1108 foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) { | |
1109 $old_value_length = strlen(trim($existing_value)); | |
1110 if (($new_value_length <= $old_value_length) && (substr($existing_value, 0, $new_value_length) == trim($value))) { | |
1111 // new value is identical but shorter-than (or equal-length to) one already in comments - skip | |
1112 break 2; | |
1113 } | |
1114 } | |
1115 } | |
1116 else { | |
1117 | |
1118 $new_value_length = strlen(trim($value)); | |
1119 foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) { | |
1120 $old_value_length = strlen(trim($existing_value)); | |
1121 if (($new_value_length > $old_value_length) && (substr(trim($value), 0, strlen($existing_value)) == $existing_value)) { | |
1122 $this->info['comments'][$tag_name][$existing_key] = trim($value); | |
1123 break 2; | |
1124 } | |
1125 } | |
1126 } | |
1127 | |
1128 if (empty($this->info['comments'][$tag_name]) || !in_array(trim($value), $this->info['comments'][$tag_name])) { | |
1129 $this->info['comments'][$tag_name][] = trim($value); | |
1130 } | |
1131 } | |
1132 } | |
1133 } | |
1134 } | |
1135 } | |
1136 | |
1137 return true; | |
1138 } | |
1139 } | |
1140 | |
1141 | |
1142 abstract class getid3_handler | |
1143 { | |
1144 | |
1145 protected $getid3; // pointer | |
1146 | |
1147 protected $data_string_flag = false; // analyzing filepointer or string | |
1148 protected $data_string; // string to analyze | |
1149 protected $data_string_position = 0; // seek position in string | |
1150 | |
1151 | |
1152 public function __construct(getID3 $getid3) { | |
1153 | |
1154 $this->getid3 = $getid3; | |
1155 } | |
1156 | |
1157 | |
1158 // Analyze from file pointer | |
1159 abstract public function Analyze(); | |
1160 | |
1161 | |
1162 | |
1163 // Analyze from string instead | |
1164 public function AnalyzeString(&$string) { | |
1165 | |
1166 // Enter string mode | |
1167 $this->data_string_flag = true; | |
1168 $this->data_string = $string; | |
1169 | |
1170 // Save info | |
1171 $saved_avdataoffset = $this->getid3->info['avdataoffset']; | |
1172 $saved_avdataend = $this->getid3->info['avdataend']; | |
1173 $saved_filesize = $this->getid3->info['filesize']; | |
1174 | |
1175 // Reset some info | |
1176 $this->getid3->info['avdataoffset'] = 0; | |
1177 $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = strlen($string); | |
1178 | |
1179 // Analyze | |
1180 $this->Analyze(); | |
1181 | |
1182 // Restore some info | |
1183 $this->getid3->info['avdataoffset'] = $saved_avdataoffset; | |
1184 $this->getid3->info['avdataend'] = $saved_avdataend; | |
1185 $this->getid3->info['filesize'] = $saved_filesize; | |
1186 | |
1187 // Exit string mode | |
1188 $this->data_string_flag = false; | |
1189 } | |
1190 | |
1191 | |
1192 protected function ftell() { | |
1193 | |
1194 if ($this->data_string_flag) { | |
1195 return $this->data_string_position; | |
1196 } | |
1197 return ftell($this->getid3->fp); | |
1198 } | |
1199 | |
1200 | |
1201 protected function fread($bytes) { | |
1202 | |
1203 if ($this->data_string_flag) { | |
1204 $this->data_string_position += $bytes; | |
1205 return substr($this->data_string, $this->data_string_position - $bytes, $bytes); | |
1206 } | |
1207 return fread($this->getid3->fp, $bytes); | |
1208 } | |
1209 | |
1210 | |
1211 protected function fseek($bytes, $whence = SEEK_SET) { | |
1212 | |
1213 if ($this->data_string_flag) { | |
1214 switch ($whence) { | |
1215 case SEEK_SET: | |
1216 $this->data_string_position = $bytes; | |
1217 return; | |
1218 | |
1219 case SEEK_CUR: | |
1220 $this->data_string_position += $bytes; | |
1221 return; | |
1222 | |
1223 case SEEK_END: | |
1224 $this->data_string_position = strlen($this->data_string) + $bytes; | |
1225 return; | |
1226 } | |
1227 } | |
1228 return fseek($this->getid3->fp, $bytes, $whence); | |
1229 } | |
1230 | |
1231 } | |
1232 | |
1233 | |
1234 | |
1235 | |
1236 abstract class getid3_handler_write | |
1237 { | |
1238 protected $filename; | |
1239 protected $user_abort; | |
1240 | |
1241 private $fp_lock; | |
1242 private $owner; | |
1243 private $group; | |
1244 private $perms; | |
1245 | |
1246 | |
1247 public function __construct($filename) { | |
1248 | |
1249 if (!file_exists($filename)) { | |
1250 throw new getid3_exception('File does not exist: "' . $filename . '"'); | |
1251 } | |
1252 | |
1253 if (!is_writeable($filename)) { | |
1254 throw new getid3_exception('File is not writeable: "' . $filename . '"'); | |
1255 } | |
1256 | |
1257 if (!is_writeable(dirname($filename))) { | |
1258 throw new getid3_exception('Directory is not writeable: ' . dirname($filename) . ' (need to write lock file).'); | |
1259 } | |
1260 | |
1261 $this->user_abort = ignore_user_abort(true); | |
1262 | |
1263 $this->fp_lock = fopen($filename . '.getid3.lock', 'w'); | |
1264 flock($this->fp_lock, LOCK_EX); | |
1265 | |
1266 $this->filename = $filename; | |
1267 } | |
1268 | |
1269 | |
1270 public function __destruct() { | |
1271 | |
1272 flock($this->fp_lock, LOCK_UN); | |
1273 fclose($this->fp_lock); | |
1274 unlink($this->filename . '.getid3.lock'); | |
1275 | |
1276 ignore_user_abort($this->user_abort); | |
1277 } | |
1278 | |
1279 | |
1280 protected function save_permissions() { | |
1281 | |
1282 $this->owner = fileowner($this->filename); | |
1283 $this->group = filegroup($this->filename); | |
1284 $this->perms = fileperms($this->filename); | |
1285 } | |
1286 | |
1287 | |
1288 protected function restore_permissions() { | |
1289 | |
1290 @chown($this->filename, $this->owner); | |
1291 @chgrp($this->filename, $this->group); | |
1292 @chmod($this->filename, $this->perms); | |
1293 } | |
1294 | |
1295 | |
1296 abstract public function read(); | |
1297 | |
1298 abstract public function write(); | |
1299 | |
1300 abstract public function remove(); | |
1301 | |
1302 } | |
1303 | |
1304 | |
1305 | |
1306 | |
1307 class getid3_exception extends Exception | |
1308 { | |
1309 public $message; | |
1310 | |
1311 } | |
1312 | |
1313 | |
1314 | |
1315 | |
1316 class getid3_lib | |
1317 { | |
1318 | |
1319 // Convert Little Endian byte string to int - max 32 bits | |
1320 public static function LittleEndian2Int($byte_word, $signed = false) { | |
1321 | |
1322 return getid3_lib::BigEndian2Int(strrev($byte_word), $signed); | |
1323 } | |
1324 | |
1325 | |
1326 | |
1327 // Convert number to Little Endian byte string | |
1328 public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { | |
1329 $intstring = ''; | |
1330 while ($number > 0) { | |
1331 if ($synchsafe) { | |
1332 $intstring = $intstring.chr($number & 127); | |
1333 $number >>= 7; | |
1334 } else { | |
1335 $intstring = $intstring.chr($number & 255); | |
1336 $number >>= 8; | |
1337 } | |
1338 } | |
1339 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); | |
1340 } | |
1341 | |
1342 | |
1343 | |
1344 // Convert Big Endian byte string to int - max 32 bits | |
1345 public static function BigEndian2Int($byte_word, $signed = false) { | |
1346 | |
1347 $int_value = 0; | |
1348 $byte_wordlen = strlen($byte_word); | |
1349 | |
1350 for ($i = 0; $i < $byte_wordlen; $i++) { | |
1351 $int_value += ord($byte_word{$i}) * pow(256, ($byte_wordlen - 1 - $i)); | |
1352 } | |
1353 | |
1354 if ($signed) { | |
1355 $sign_mask_bit = 0x80 << (8 * ($byte_wordlen - 1)); | |
1356 if ($int_value & $sign_mask_bit) { | |
1357 $int_value = 0 - ($int_value & ($sign_mask_bit - 1)); | |
1358 } | |
1359 } | |
1360 | |
1361 return $int_value; | |
1362 } | |
1363 | |
1364 | |
1365 | |
1366 // Convert Big Endian byte sybc safe string to int - max 32 bits | |
1367 public static function BigEndianSyncSafe2Int($byte_word) { | |
1368 | |
1369 $int_value = 0; | |
1370 $byte_wordlen = strlen($byte_word); | |
1371 | |
1372 // disregard MSB, effectively 7-bit bytes | |
1373 for ($i = 0; $i < $byte_wordlen; $i++) { | |
1374 $int_value = $int_value | (ord($byte_word{$i}) & 0x7F) << (($byte_wordlen - 1 - $i) * 7); | |
1375 } | |
1376 return $int_value; | |
1377 } | |
1378 | |
1379 | |
1380 | |
1381 // Convert Big Endian byte string to bit string | |
1382 public static function BigEndian2Bin($byte_word) { | |
1383 | |
1384 $bin_value = ''; | |
1385 $byte_wordlen = strlen($byte_word); | |
1386 for ($i = 0; $i < $byte_wordlen; $i++) { | |
1387 $bin_value .= str_pad(decbin(ord($byte_word{$i})), 8, '0', STR_PAD_LEFT); | |
1388 } | |
1389 return $bin_value; | |
1390 } | |
1391 | |
1392 | |
1393 | |
1394 public static function BigEndian2Float($byte_word) { | |
1395 | |
1396 // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic | |
1397 // http://www.psc.edu/general/software/packages/ieee/ieee.html | |
1398 // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html | |
1399 | |
1400 $bit_word = getid3_lib::BigEndian2Bin($byte_word); | |
1401 if (!$bit_word) { | |
1402 return 0; | |
1403 } | |
1404 $sign_bit = $bit_word{0}; | |
1405 | |
1406 switch (strlen($byte_word) * 8) { | |
1407 case 32: | |
1408 $exponent_bits = 8; | |
1409 $fraction_bits = 23; | |
1410 break; | |
1411 | |
1412 case 64: | |
1413 $exponent_bits = 11; | |
1414 $fraction_bits = 52; | |
1415 break; | |
1416 | |
1417 case 80: | |
1418 // 80-bit Apple SANE format | |
1419 // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ | |
1420 $exponent_string = substr($bit_word, 1, 15); | |
1421 $is_normalized = intval($bit_word{16}); | |
1422 $fraction_string = substr($bit_word, 17, 63); | |
1423 $exponent = pow(2, getid3_lib::Bin2Dec($exponent_string) - 16383); | |
1424 $fraction = $is_normalized + getid3_lib::DecimalBinary2Float($fraction_string); | |
1425 $float_value = $exponent * $fraction; | |
1426 if ($sign_bit == '1') { | |
1427 $float_value *= -1; | |
1428 } | |
1429 return $float_value; | |
1430 break; | |
1431 | |
1432 default: | |
1433 return false; | |
1434 break; | |
1435 } | |
1436 $exponent_string = substr($bit_word, 1, $exponent_bits); | |
1437 $fraction_string = substr($bit_word, $exponent_bits + 1, $fraction_bits); | |
1438 $exponent = bindec($exponent_string); | |
1439 $fraction = bindec($fraction_string); | |
1440 | |
1441 if (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction != 0)) { | |
1442 // Not a Number | |
1443 $float_value = false; | |
1444 } elseif (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction == 0)) { | |
1445 if ($sign_bit == '1') { | |
1446 $float_value = '-infinity'; | |
1447 } else { | |
1448 $float_value = '+infinity'; | |
1449 } | |
1450 } elseif (($exponent == 0) && ($fraction == 0)) { | |
1451 if ($sign_bit == '1') { | |
1452 $float_value = -0; | |
1453 } else { | |
1454 $float_value = 0; | |
1455 } | |
1456 $float_value = ($sign_bit ? 0 : -0); | |
1457 } elseif (($exponent == 0) && ($fraction != 0)) { | |
1458 // These are 'unnormalized' values | |
1459 $float_value = pow(2, (-1 * (pow(2, $exponent_bits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fraction_string); | |
1460 if ($sign_bit == '1') { | |
1461 $float_value *= -1; | |
1462 } | |
1463 } elseif ($exponent != 0) { | |
1464 $float_value = pow(2, ($exponent - (pow(2, $exponent_bits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fraction_string)); | |
1465 if ($sign_bit == '1') { | |
1466 $float_value *= -1; | |
1467 } | |
1468 } | |
1469 return (float) $float_value; | |
1470 } | |
1471 | |
1472 | |
1473 | |
1474 public static function LittleEndian2Float($byte_word) { | |
1475 | |
1476 return getid3_lib::BigEndian2Float(strrev($byte_word)); | |
1477 } | |
1478 | |
1479 | |
1480 | |
1481 public static function DecimalBinary2Float($binary_numerator) { | |
1482 $numerator = bindec($binary_numerator); | |
1483 $denominator = bindec('1'.str_repeat('0', strlen($binary_numerator))); | |
1484 return ($numerator / $denominator); | |
1485 } | |
1486 | |
1487 | |
1488 public static function PrintHexBytes($string, $hex=true, $spaces=true, $html_safe=true) { | |
1489 | |
1490 $return_string = ''; | |
1491 for ($i = 0; $i < strlen($string); $i++) { | |
1492 if ($hex) { | |
1493 $return_string .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT); | |
1494 } else { | |
1495 $return_string .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : ''); | |
1496 } | |
1497 if ($spaces) { | |
1498 $return_string .= ' '; | |
1499 } | |
1500 } | |
1501 if ($html_safe) { | |
1502 $return_string = htmlentities($return_string); | |
1503 } | |
1504 return $return_string; | |
1505 } | |
1506 | |
1507 | |
1508 | |
1509 // Process header data string - read several values with algorithm and add to target | |
1510 // algorithm is one one the getid3_lib::Something2Something() function names | |
1511 // parts_array is index => length - $target[index] = algorithm(substring(data)) | |
1512 // - OR just substring(data) if length is negative! | |
1513 // indexes == 'IGNORE**' are ignored | |
1514 | |
1515 public static function ReadSequence($algorithm, &$target, &$data, $offset, $parts_array) { | |
1516 | |
1517 // Loop thru $parts_array | |
1518 foreach ($parts_array as $target_string => $length) { | |
1519 | |
1520 // Add to target | |
1521 if (!strstr($target_string, 'IGNORE')) { | |
1522 | |
1523 // substr(....length) | |
1524 if ($length < 0) { | |
1525 $target[$target_string] = substr($data, $offset, -$length); | |
1526 } | |
1527 | |
1528 // algorithm(substr(...length)) | |
1529 else { | |
1530 $target[$target_string] = getid3_lib::$algorithm(substr($data, $offset, $length)); | |
1531 } | |
1532 } | |
1533 | |
1534 // Move pointer | |
1535 $offset += abs($length); | |
1536 } | |
1537 } | |
1538 | |
1539 } | |
1540 | |
1541 | |
1542 | |
1543 class getid3_lib_replaygain | |
1544 { | |
1545 | |
1546 public static function NameLookup($name_code) { | |
1547 | |
1548 static $lookup = array ( | |
1549 0 => 'not set', | |
1550 1 => 'Track Gain Adjustment', | |
1551 2 => 'Album Gain Adjustment' | |
1552 ); | |
1553 | |
1554 return @$lookup[$name_code]; | |
1555 } | |
1556 | |
1557 | |
1558 | |
1559 public static function OriginatorLookup($originator_code) { | |
1560 | |
1561 static $lookup = array ( | |
1562 0 => 'unspecified', | |
1563 1 => 'pre-set by artist/producer/mastering engineer', | |
1564 2 => 'set by user', | |
1565 3 => 'determined automatically' | |
1566 ); | |
1567 | |
1568 return @$lookup[$originator_code]; | |
1569 } | |
1570 | |
1571 | |
1572 | |
1573 public static function AdjustmentLookup($raw_adjustment, $sign_bit) { | |
1574 | |
1575 return (float)$raw_adjustment / 10 * ($sign_bit == 1 ? -1 : 1); | |
1576 } | |
1577 | |
1578 | |
1579 | |
1580 public static function GainString($name_code, $originator_code, $replaygain) { | |
1581 | |
1582 $sign_bit = $replaygain < 0 ? 1 : 0; | |
1583 | |
1584 $stored_replaygain = intval(round($replaygain * 10)); | |
1585 $gain_string = str_pad(decbin($name_code), 3, '0', STR_PAD_LEFT); | |
1586 $gain_string .= str_pad(decbin($originator_code), 3, '0', STR_PAD_LEFT); | |
1587 $gain_string .= $sign_bit; | |
1588 $gain_string .= str_pad(decbin($stored_replaygain), 9, '0', STR_PAD_LEFT); | |
1589 | |
1590 return $gain_string; | |
1591 } | |
1592 | |
1593 } | |
1594 | |
1595 | |
1596 | |
1597 | |
1598 ?> |