Ampache PHP Cross Reference Groupware Applications

Source: /modules/getid3/write.apetag.php - 224 lines - 7600 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  //          also https://github.com/JamesHeinrich/getID3       //
   7  /////////////////////////////////////////////////////////////////
   8  // See readme.txt for more details                             //
   9  /////////////////////////////////////////////////////////////////
  10  //                                                             //
  11  // write.apetag.php                                            //
  12  // module for writing APE tags                                 //
  13  // dependencies: module.tag.apetag.php                         //
  14  //                                                            ///
  15  /////////////////////////////////////////////////////////////////
  16  
  17  
  18  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
  19  
  20  class getid3_write_apetag
  21  {
  22  
  23      public $filename;
  24      public $tag_data;
  25      public $always_preserve_replaygain = true;    // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
  26      public $warnings                   = array(); // any non-critical errors will be stored here
  27      public $errors                     = array(); // any critical errors will be stored here
  28  
  29  	public function getid3_write_apetag() {
  30          return true;
  31      }
  32  
  33  	public function WriteAPEtag() {
  34          // NOTE: All data passed to this function must be UTF-8 format
  35  
  36          $getID3 = new getID3;
  37          $ThisFileInfo = $getID3->analyze($this->filename);
  38  
  39          if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
  40              if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
  41                  // Current APE tag between Lyrics3 and ID3v1/EOF
  42                  // This break Lyrics3 functionality
  43                  if (!$this->DeleteAPEtag()) {
  44                      return false;
  45                  }
  46                  $ThisFileInfo = $getID3->analyze($this->filename);
  47              }
  48          }
  49  
  50          if ($this->always_preserve_replaygain) {
  51              $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
  52              foreach ($ReplayGainTagsToPreserve as $rg_key) {
  53                  if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
  54                      $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
  55                  }
  56              }
  57          }
  58  
  59          if ($APEtag = $this->GenerateAPEtag()) {
  60              if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
  61                  $oldignoreuserabort = ignore_user_abort(true);
  62                  flock($fp, LOCK_EX);
  63  
  64                  $PostAPEdataOffset = $ThisFileInfo['avdataend'];
  65                  if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
  66                      $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
  67                  }
  68                  if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
  69                      $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
  70                  }
  71                  fseek($fp, $PostAPEdataOffset);
  72                  $PostAPEdata = '';
  73                  if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
  74                      $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
  75                  }
  76  
  77                  fseek($fp, $PostAPEdataOffset);
  78                  if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
  79                      fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
  80                  }
  81                  ftruncate($fp, ftell($fp));
  82                  fwrite($fp, $APEtag, strlen($APEtag));
  83                  if (!empty($PostAPEdata)) {
  84                      fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
  85                  }
  86                  flock($fp, LOCK_UN);
  87                  fclose($fp);
  88                  ignore_user_abort($oldignoreuserabort);
  89                  return true;
  90              }
  91          }
  92          return false;
  93      }
  94  
  95  	public function DeleteAPEtag() {
  96          $getID3 = new getID3;
  97          $ThisFileInfo = $getID3->analyze($this->filename);
  98          if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
  99              if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
 100  
 101                  flock($fp, LOCK_EX);
 102                  $oldignoreuserabort = ignore_user_abort(true);
 103  
 104                  fseek($fp, $ThisFileInfo['ape']['tag_offset_end']);
 105                  $DataAfterAPE = '';
 106                  if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
 107                      $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
 108                  }
 109  
 110                  ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
 111                  fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
 112  
 113                  if (!empty($DataAfterAPE)) {
 114                      fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
 115                  }
 116  
 117                  flock($fp, LOCK_UN);
 118                  fclose($fp);
 119                  ignore_user_abort($oldignoreuserabort);
 120  
 121                  return true;
 122              }
 123              return false;
 124          }
 125          return true;
 126      }
 127  
 128  
 129  	public function GenerateAPEtag() {
 130          // NOTE: All data passed to this function must be UTF-8 format
 131  
 132          $items = array();
 133          if (!is_array($this->tag_data)) {
 134              return false;
 135          }
 136          foreach ($this->tag_data as $key => $arrayofvalues) {
 137              if (!is_array($arrayofvalues)) {
 138                  return false;
 139              }
 140  
 141              $valuestring = '';
 142              foreach ($arrayofvalues as $value) {
 143                  $valuestring .= str_replace("\x00", '', $value)."\x00";
 144              }
 145              $valuestring = rtrim($valuestring, "\x00");
 146  
 147              // Length of the assigned value in bytes
 148              $tagitem  = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
 149  
 150              //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
 151              $tagitem .= "\x00\x00\x00\x00";
 152  
 153              $tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
 154              $tagitem .= $valuestring;
 155  
 156              $items[] = $tagitem;
 157  
 158          }
 159  
 160          return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
 161      }
 162  
 163  	public function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
 164          $tagdatalength = 0;
 165          foreach ($items as $itemdata) {
 166              $tagdatalength += strlen($itemdata);
 167          }
 168  
 169          $APEheader  = 'APETAGEX';
 170          $APEheader .= getid3_lib::LittleEndian2String(2000, 4);
 171          $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
 172          $APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
 173          $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
 174          $APEheader .= str_repeat("\x00", 8);
 175  
 176          return $APEheader;
 177      }
 178  
 179  	public function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
 180          $APEtagFlags = array_fill(0, 4, 0);
 181          if ($header) {
 182              $APEtagFlags[0] |= 0x80; // Tag contains a header
 183          }
 184          if (!$footer) {
 185              $APEtagFlags[0] |= 0x40; // Tag contains no footer
 186          }
 187          if ($isheader) {
 188              $APEtagFlags[0] |= 0x20; // This is the header, not the footer
 189          }
 190  
 191          // 0: Item contains text information coded in UTF-8
 192          // 1: Item contains binary information °)
 193          // 2: Item is a locator of external stored information °°)
 194          // 3: reserved
 195          $APEtagFlags[3] |= ($encodingid << 1);
 196  
 197          if ($readonly) {
 198              $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
 199          }
 200  
 201          return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
 202      }
 203  
 204  	public function CleanAPEtagItemKey($itemkey) {
 205          $itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
 206  
 207          // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
 208          switch (strtoupper($itemkey)) {
 209              case 'EAN/UPC':
 210              case 'ISBN':
 211              case 'LC':
 212              case 'ISRC':
 213                  $itemkey = strtoupper($itemkey);
 214                  break;
 215  
 216              default:
 217                  $itemkey = ucwords($itemkey);
 218                  break;
 219          }
 220          return $itemkey;
 221  
 222      }
 223  
 224  }

title

Description

title

Description

title

Description

title

title

Body