[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 <?php 2 ///////////////////////////////////////////////////////////////// 3 /// getID3() by James Heinrich <info@getid3.org> // 4 // available at http://getid3.sourceforge.net // 5 // or http://www.getid3.org // 6 ///////////////////////////////////////////////////////////////// 7 // See readme.txt for more details // 8 ///////////////////////////////////////////////////////////////// 9 // // 10 // module.tag.apetag.php // 11 // module for analyzing APE tags // 12 // dependencies: NONE // 13 // /// 14 ///////////////////////////////////////////////////////////////// 15 16 class getid3_apetag extends getid3_handler 17 { 18 public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory 19 public $overrideendoffset = 0; 20 21 public function Analyze() { 22 $info = &$this->getid3->info; 23 24 if (!getid3_lib::intValueSupported($info['filesize'])) { 25 $info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; 26 return false; 27 } 28 29 $id3v1tagsize = 128; 30 $apetagheadersize = 32; 31 $lyrics3tagsize = 10; 32 33 if ($this->overrideendoffset == 0) { 34 35 fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); 36 $APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); 37 38 //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { 39 if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { 40 41 // APE tag found before ID3v1 42 $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize; 43 44 //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { 45 } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { 46 47 // APE tag found, no ID3v1 48 $info['ape']['tag_offset_end'] = $info['filesize']; 49 50 } 51 52 } else { 53 54 fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET); 55 if (fread($this->getid3->fp, 8) == 'APETAGEX') { 56 $info['ape']['tag_offset_end'] = $this->overrideendoffset; 57 } 58 59 } 60 if (!isset($info['ape']['tag_offset_end'])) { 61 62 // APE tag not found 63 unset($info['ape']); 64 return false; 65 66 } 67 68 // shortcut 69 $thisfile_ape = &$info['ape']; 70 71 fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); 72 $APEfooterData = fread($this->getid3->fp, 32); 73 if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { 74 $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; 75 return false; 76 } 77 78 if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { 79 fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); 80 $thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp); 81 $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); 82 } else { 83 $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; 84 fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET); 85 $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']); 86 } 87 $info['avdataend'] = $thisfile_ape['tag_offset_start']; 88 89 if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { 90 $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; 91 unset($info['id3v1']); 92 foreach ($info['warning'] as $key => $value) { 93 if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { 94 unset($info['warning'][$key]); 95 sort($info['warning']); 96 break; 97 } 98 } 99 } 100 101 $offset = 0; 102 if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { 103 if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { 104 $offset += $apetagheadersize; 105 } else { 106 $info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; 107 return false; 108 } 109 } 110 111 // shortcut 112 $info['replay_gain'] = array(); 113 $thisfile_replaygain = &$info['replay_gain']; 114 115 for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { 116 $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); 117 $offset += 4; 118 $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); 119 $offset += 4; 120 if (strstr(substr($APEtagData, $offset), "\x00") === false) { 121 $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset); 122 return false; 123 } 124 $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset; 125 $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); 126 127 // shortcut 128 $thisfile_ape['items'][$item_key] = array(); 129 $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key]; 130 131 $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset; 132 133 $offset += ($ItemKeyLength + 1); // skip 0x00 terminator 134 $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); 135 $offset += $value_size; 136 137 $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); 138 switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { 139 case 0: // UTF-8 140 case 3: // Locator (URL, filename, etc), UTF-8 encoded 141 $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); 142 break; 143 144 default: // binary data 145 break; 146 } 147 148 switch (strtolower($item_key)) { 149 case 'replaygain_track_gain': 150 $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 151 $thisfile_replaygain['track']['originator'] = 'unspecified'; 152 break; 153 154 case 'replaygain_track_peak': 155 $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 156 $thisfile_replaygain['track']['originator'] = 'unspecified'; 157 if ($thisfile_replaygain['track']['peak'] <= 0) { 158 $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; 159 } 160 break; 161 162 case 'replaygain_album_gain': 163 $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 164 $thisfile_replaygain['album']['originator'] = 'unspecified'; 165 break; 166 167 case 'replaygain_album_peak': 168 $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 169 $thisfile_replaygain['album']['originator'] = 'unspecified'; 170 if ($thisfile_replaygain['album']['peak'] <= 0) { 171 $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; 172 } 173 break; 174 175 case 'mp3gain_undo': 176 list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); 177 $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); 178 $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); 179 $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); 180 break; 181 182 case 'mp3gain_minmax': 183 list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); 184 $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); 185 $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); 186 break; 187 188 case 'mp3gain_album_minmax': 189 list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); 190 $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); 191 $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); 192 break; 193 194 case 'tracknumber': 195 if (is_array($thisfile_ape_items_current['data'])) { 196 foreach ($thisfile_ape_items_current['data'] as $comment) { 197 $thisfile_ape['comments']['track'][] = $comment; 198 } 199 } 200 break; 201 202 case 'cover art (artist)': 203 case 'cover art (back)': 204 case 'cover art (band logo)': 205 case 'cover art (band)': 206 case 'cover art (colored fish)': 207 case 'cover art (composer)': 208 case 'cover art (conductor)': 209 case 'cover art (front)': 210 case 'cover art (icon)': 211 case 'cover art (illustration)': 212 case 'cover art (lead)': 213 case 'cover art (leaflet)': 214 case 'cover art (lyricist)': 215 case 'cover art (media)': 216 case 'cover art (movie scene)': 217 case 'cover art (other icon)': 218 case 'cover art (other)': 219 case 'cover art (performance)': 220 case 'cover art (publisher logo)': 221 case 'cover art (recording)': 222 case 'cover art (studio)': 223 // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html 224 list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2); 225 $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); 226 $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); 227 228 $thisfile_ape_items_current['image_mime'] = ''; 229 $imageinfo = array(); 230 $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); 231 $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); 232 233 do { 234 if ($this->inline_attachments === false) { 235 // skip entirely 236 unset($thisfile_ape_items_current['data']); 237 break; 238 } 239 if ($this->inline_attachments === true) { 240 // great 241 } elseif (is_int($this->inline_attachments)) { 242 if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) { 243 // too big, skip 244 $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)'; 245 unset($thisfile_ape_items_current['data']); 246 break; 247 } 248 } elseif (is_string($this->inline_attachments)) { 249 $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); 250 if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { 251 // cannot write, skip 252 $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; 253 unset($thisfile_ape_items_current['data']); 254 break; 255 } 256 } 257 // if we get this far, must be OK 258 if (is_string($this->inline_attachments)) { 259 $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset']; 260 if (!file_exists($destination_filename) || is_writable($destination_filename)) { 261 file_put_contents($destination_filename, $thisfile_ape_items_current['data']); 262 } else { 263 $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)'; 264 } 265 $thisfile_ape_items_current['data_filename'] = $destination_filename; 266 unset($thisfile_ape_items_current['data']); 267 } else { 268 if (!isset($info['ape']['comments']['picture'])) { 269 $info['ape']['comments']['picture'] = array(); 270 } 271 $info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']); 272 } 273 } while (false); 274 break; 275 276 default: 277 if (is_array($thisfile_ape_items_current['data'])) { 278 foreach ($thisfile_ape_items_current['data'] as $comment) { 279 $thisfile_ape['comments'][strtolower($item_key)][] = $comment; 280 } 281 } 282 break; 283 } 284 285 } 286 if (empty($thisfile_replaygain)) { 287 unset($info['replay_gain']); 288 } 289 return true; 290 } 291 292 public function parseAPEheaderFooter($APEheaderFooterData) { 293 // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html 294 295 // shortcut 296 $headerfooterinfo['raw'] = array(); 297 $headerfooterinfo_raw = &$headerfooterinfo['raw']; 298 299 $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8); 300 if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') { 301 return false; 302 } 303 $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4)); 304 $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4)); 305 $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4)); 306 $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4)); 307 $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8); 308 309 $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000; 310 if ($headerfooterinfo['tag_version'] >= 2) { 311 $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']); 312 } 313 return $headerfooterinfo; 314 } 315 316 public function parseAPEtagFlags($rawflagint) { 317 // "Note: APE Tags 1.0 do not use any of the APE Tag flags. 318 // All are set to zero on creation and ignored on reading." 319 // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html 320 $flags['header'] = (bool) ($rawflagint & 0x80000000); 321 $flags['footer'] = (bool) ($rawflagint & 0x40000000); 322 $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); 323 $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1; 324 $flags['read_only'] = (bool) ($rawflagint & 0x00000001); 325 326 $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']); 327 328 return $flags; 329 } 330 331 public function APEcontentTypeFlagLookup($contenttypeid) { 332 static $APEcontentTypeFlagLookup = array( 333 0 => 'utf-8', 334 1 => 'binary', 335 2 => 'external', 336 3 => 'reserved' 337 ); 338 return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); 339 } 340 341 public function APEtagItemIsUTF8Lookup($itemkey) { 342 static $APEtagItemIsUTF8Lookup = array( 343 'title', 344 'subtitle', 345 'artist', 346 'album', 347 'debut album', 348 'publisher', 349 'conductor', 350 'track', 351 'composer', 352 'comment', 353 'copyright', 354 'publicationright', 355 'file', 356 'year', 357 'record date', 358 'record location', 359 'genre', 360 'media', 361 'related', 362 'isrc', 363 'abstract', 364 'language', 365 'bibliography' 366 ); 367 return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); 368 } 369 370 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |