Loudblog PHP Cross Reference Blogging Systems

Source: /loudblog/inc/id3/module.tag.apetag.php - 284 lines - 11649 bytes - Summary - Text - Print

   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
  17  {
  18  
  19  	function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) {
  20          $id3v1tagsize     = 128;
  21          $apetagheadersize = 32;
  22          $lyrics3tagsize   = 10;
  23  
  24          if ($overrideendoffset == 0) {
  25  
  26              fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
  27              $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
  28  
  29              //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
  30              if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
  31  
  32                  // APE tag found before ID3v1
  33                  $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
  34  
  35              //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
  36              } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
  37  
  38                  // APE tag found, no ID3v1
  39                  $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
  40  
  41              }
  42  
  43          } else {
  44  
  45              fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
  46              if (fread($fd, 8) == 'APETAGEX') {
  47                  $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
  48              }
  49  
  50          }
  51          if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
  52  
  53              // APE tag not found
  54              unset($ThisFileInfo['ape']);
  55              return false;
  56  
  57          }
  58  
  59          // shortcut
  60          $thisfile_ape = &$ThisFileInfo['ape'];
  61  
  62          fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
  63          $APEfooterData = fread($fd, 32);
  64          if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
  65              $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
  66              return false;
  67          }
  68  
  69          if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
  70              fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
  71              $thisfile_ape['tag_offset_start'] = ftell($fd);
  72              $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
  73          } else {
  74              $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
  75              fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
  76              $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
  77          }
  78          $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
  79  
  80          if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
  81              $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
  82              unset($ThisFileInfo['id3v1']);
  83              foreach ($ThisFileInfo['warning'] as $key => $value) {
  84                  if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
  85                      unset($ThisFileInfo['warning'][$key]);
  86                      sort($ThisFileInfo['warning']);
  87                      break;
  88                  }
  89              }
  90          }
  91  
  92          $offset = 0;
  93          if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
  94              if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
  95                  $offset += $apetagheadersize;
  96              } else {
  97                  $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
  98                  return false;
  99              }
 100          }
 101  
 102          // shortcut
 103          $ThisFileInfo['replay_gain'] = array();
 104          $thisfile_replaygain = &$ThisFileInfo['replay_gain'];
 105          
 106          for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
 107              $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
 108              $offset += 4;
 109              $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
 110              $offset += 4;
 111              if (strstr(substr($APEtagData, $offset), "\x00") === false) {
 112                  $ThisFileInfo['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);
 113                  return false;
 114              }
 115              $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
 116              $item_key      = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
 117  
 118              // shortcut
 119              $thisfile_ape['items'][$item_key] = array();
 120              $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
 121  
 122              $offset += ($ItemKeyLength + 1); // skip 0x00 terminator
 123              $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
 124              $offset += $value_size;
 125  
 126              $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
 127              switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
 128                  case 0: // UTF-8
 129                  case 3: // Locator (URL, filename, etc), UTF-8 encoded
 130                      $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
 131                      break;
 132  
 133                  default: // binary data
 134                      break;
 135              }
 136  
 137              switch (strtolower($item_key)) {
 138                  case 'replaygain_track_gain':
 139                      $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
 140                      $thisfile_replaygain['track']['originator'] = 'unspecified';
 141                      break;
 142  
 143                  case 'replaygain_track_peak':
 144                      $thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
 145                      $thisfile_replaygain['track']['originator'] = 'unspecified';
 146                      if ($thisfile_replaygain['track']['peak'] <= 0) {
 147                          $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
 148                      }
 149                      break;
 150  
 151                  case 'replaygain_album_gain':
 152                      $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
 153                      $thisfile_replaygain['album']['originator'] = 'unspecified';
 154                      break;
 155  
 156                  case 'replaygain_album_peak':
 157                      $thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
 158                      $thisfile_replaygain['album']['originator'] = 'unspecified';
 159                      if ($thisfile_replaygain['album']['peak'] <= 0) {
 160                          $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
 161                      }
 162                      break;
 163  
 164                  case 'mp3gain_undo':
 165                      list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
 166                      $thisfile_replaygain['mp3gain']['undo_left']  = intval($mp3gain_undo_left);
 167                      $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
 168                      $thisfile_replaygain['mp3gain']['undo_wrap']  = (($mp3gain_undo_wrap == 'Y') ? true : false);
 169                      break;
 170  
 171                  case 'mp3gain_minmax':
 172                      list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
 173                      $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
 174                      $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
 175                      break;
 176  
 177                  case 'mp3gain_album_minmax':
 178                      list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
 179                      $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
 180                      $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
 181                      break;
 182  
 183                  case 'tracknumber':
 184                      foreach ($thisfile_ape_items_current['data'] as $comment) {
 185                          $thisfile_ape['comments']['track'][] = $comment;
 186                      }
 187                      break;
 188  
 189                  default:
 190                      foreach ($thisfile_ape_items_current['data'] as $comment) {
 191                          $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
 192                      }
 193                      break;
 194              }
 195  
 196          }
 197          if (empty($thisfile_replaygain)) {
 198              unset($ThisFileInfo['replay_gain']);
 199          }
 200  
 201          return true;
 202      }
 203  
 204  	function parseAPEheaderFooter($APEheaderFooterData) {
 205          // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
 206  
 207          // shortcut
 208          $headerfooterinfo['raw'] = array();
 209          $headerfooterinfo_raw = &$headerfooterinfo['raw'];
 210  
 211          $headerfooterinfo_raw['footer_tag']   =                  substr($APEheaderFooterData,  0, 8);
 212          if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
 213              return false;
 214          }
 215          $headerfooterinfo_raw['version']      = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData,  8, 4));
 216          $headerfooterinfo_raw['tagsize']      = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
 217          $headerfooterinfo_raw['tag_items']    = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
 218          $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
 219          $headerfooterinfo_raw['reserved']     =                              substr($APEheaderFooterData, 24, 8);
 220  
 221          $headerfooterinfo['tag_version']         = $headerfooterinfo_raw['version'] / 1000;
 222          if ($headerfooterinfo['tag_version'] >= 2) {
 223              $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
 224          }
 225          return $headerfooterinfo;
 226      }
 227  
 228  	function parseAPEtagFlags($rawflagint) {
 229          // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
 230          // All are set to zero on creation and ignored on reading."
 231          // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
 232          $flags['header']            = (bool) ($rawflagint & 0x80000000);
 233          $flags['footer']            = (bool) ($rawflagint & 0x40000000);
 234          $flags['this_is_header']    = (bool) ($rawflagint & 0x20000000);
 235          $flags['item_contents_raw'] =        ($rawflagint & 0x00000006) >> 1;
 236          $flags['read_only']         = (bool) ($rawflagint & 0x00000001);
 237  
 238          $flags['item_contents']     = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
 239  
 240          return $flags;
 241      }
 242  
 243  	function APEcontentTypeFlagLookup($contenttypeid) {
 244          static $APEcontentTypeFlagLookup = array(
 245              0 => 'utf-8',
 246              1 => 'binary',
 247              2 => 'external',
 248              3 => 'reserved'
 249          );
 250          return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
 251      }
 252  
 253  	function APEtagItemIsUTF8Lookup($itemkey) {
 254          static $APEtagItemIsUTF8Lookup = array(
 255              'title',
 256              'subtitle',
 257              'artist',
 258              'album',
 259              'debut album',
 260              'publisher',
 261              'conductor',
 262              'track',
 263              'composer',
 264              'comment',
 265              'copyright',
 266              'publicationright',
 267              'file',
 268              'year',
 269              'record date',
 270              'record location',
 271              'genre',
 272              'media',
 273              'related',
 274              'isrc',
 275              'abstract',
 276              'language',
 277              'bibliography'
 278          );
 279          return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
 280      }
 281  
 282  }
 283  
 284  ?>

title

Description

title

Description

title

Description

title

title

Body