PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/common/libraries/plugin/getid3/write.apetag.php

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